Add LdapClient

Closes gh-675
This commit is contained in:
Josh Cummings
2023-03-16 12:21:36 -06:00
parent 19f0eccd90
commit de86a00e8c
21 changed files with 4781 additions and 130 deletions

View File

@@ -28,14 +28,14 @@ import org.springframework.ldap.core.AttributesMapper;
* @author Mattias Hellborg Arthursson
*
*/
public class PersonAttributesMapper implements AttributesMapper {
public class PersonAttributesMapper implements AttributesMapper<Person> {
/**
* Maps the given attributes into a {@link Person} object.
*
* @see org.springframework.ldap.core.AttributesMapper#mapFromAttributes(javax.naming.directory.Attributes)
*/
public Object mapFromAttributes(Attributes attributes)
public Person mapFromAttributes(Attributes attributes)
throws NamingException {
Person person = new Person();
person.setFullname((String) attributes.get("cn").get());

View File

@@ -25,9 +25,9 @@ import org.springframework.ldap.core.support.AbstractContextMapper;
*
* @author Mattias Hellborg Arthursson
*/
public class PersonContextMapper extends AbstractContextMapper {
public class PersonContextMapper extends AbstractContextMapper<Person> {
protected Object doMapFromContext(DirContextOperations ctx) {
protected Person doMapFromContext(DirContextOperations ctx) {
Person person = new Person();
person.setFullname(ctx.getStringAttribute("cn"));
person.setLastname(ctx.getStringAttribute("sn"));

View File

@@ -0,0 +1,179 @@
/*
* Copyright 2005-2023 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
*
* https://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.itest;
import javax.naming.NamingException;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.dao.IncorrectResultSizeDataAccessException;
import org.springframework.ldap.AuthenticationException;
import org.springframework.ldap.core.AuthenticatedLdapEntryContextCallback;
import org.springframework.ldap.core.DirContextAdapter;
import org.springframework.ldap.core.DirContextOperations;
import org.springframework.ldap.core.LdapClient;
import org.springframework.ldap.core.support.LookupAttemptingCallback;
import org.springframework.ldap.filter.AndFilter;
import org.springframework.ldap.filter.EqualsFilter;
import org.springframework.ldap.query.LdapQuery;
import org.springframework.ldap.query.LdapQueryBuilder;
import org.springframework.ldap.support.LdapUtils;
import org.springframework.test.context.ContextConfiguration;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
/**
* Tests for {@link LdapClient}'s authenticate methods.
*
* @author Josh Cummings
*/
@ContextConfiguration(locations = {"/conf/ldapClientTestContext.xml"})
public class DefaultLdapClientAuthenticationITest extends AbstractLdapTemplateIntegrationTest {
@Autowired
private LdapClient tested;
@Test
@Category(NoAdTest.class)
public void testAuthenticate() {
AndFilter filter = new AndFilter();
filter.and(new EqualsFilter("objectclass", "person")).and(new EqualsFilter("uid", "some.person3"));
LdapQuery query = LdapQueryBuilder.query().base(LdapUtils.emptyLdapName()).filter(filter);
tested.authenticate().query(query).password("password").execute();
}
@Test
@Category(NoAdTest.class)
public void testAuthenticateWithLdapQuery() {
AndFilter filter = new AndFilter();
filter.and(new EqualsFilter("objectclass", "person")).and(new EqualsFilter("uid", "some.person3"));
LdapQuery query = LdapQueryBuilder.query()
.where("objectclass").is("person")
.and("uid").is("some.person3");
tested.authenticate().query(query).password("password").execute();
}
@Test
@Category(NoAdTest.class)
public void testAuthenticateWithInvalidPassword() {
AndFilter filter = new AndFilter();
filter.and(new EqualsFilter("objectclass", "person")).and(new EqualsFilter("uid", "some.person3"));
LdapQuery query = LdapQueryBuilder.query().filter(filter);
assertThatExceptionOfType(AuthenticationException.class).isThrownBy(() ->
tested.authenticate().query(query).password("invalidpassword").execute());
}
@Test
@Category(NoAdTest.class)
public void testAuthenticateWithLdapQueryAndInvalidPassword() {
AndFilter filter = new AndFilter();
filter.and(new EqualsFilter("objectclass", "person")).and(new EqualsFilter("uid", "some.person3"));
LdapQuery query = LdapQueryBuilder.query()
.where("objectclass").is("person")
.and("uid").is("some.person3");
assertThatExceptionOfType(AuthenticationException.class).isThrownBy(() ->
tested.authenticate().query(query).password("invalidpassword").execute());
}
@Test
@Category(NoAdTest.class)
public void testAuthenticateWithLookupOperationPerformedOnAuthenticatedContext() {
AndFilter filter = new AndFilter();
filter.and(new EqualsFilter("objectclass", "person")).and(new EqualsFilter("uid", "some.person3"));
LdapQuery query = LdapQueryBuilder.query().filter(filter);
AuthenticatedLdapEntryContextCallback contextCallback = (ctx, entry) -> {
try {
DirContextAdapter adapter = (DirContextAdapter) ctx.lookup(entry.getRelativeDn());
assertThat(adapter.getStringAttribute("cn")).isEqualTo("Some Person3");
} catch (NamingException e) {
throw new RuntimeException("Failed to lookup " + entry.getRelativeDn(), e);
}
};
tested.authenticate().query(query).password("password").execute((ctx, entry) -> {
contextCallback.executeWithContext(ctx, entry);
return null;
});
}
@Test
@Category(NoAdTest.class)
public void testAuthenticateWithLdapQueryAndMapper() {
LdapQuery query = LdapQueryBuilder.query()
.where("objectclass").is("person")
.and("uid").is("some.person3");
DirContextOperations ctx = tested.authenticate().query(query).password("password")
.execute(new LookupAttemptingCallback());
assertThat(ctx).isNotNull();
assertThat(ctx.getStringAttribute("uid")).isEqualTo("some.person3");
}
@Test
@Category(NoAdTest.class)
public void testAuthenticateWithLdapQueryAndMapperAndInvalidPassword() {
LdapQuery query = LdapQueryBuilder.query()
.where("objectclass").is("person")
.and("uid").is("some.person3");
assertThatExceptionOfType(AuthenticationException.class).isThrownBy(() ->
tested.authenticate().query(query).password("invalidpassword").execute(new LookupAttemptingCallback()));
}
@Test
@Category(NoAdTest.class)
public void testAuthenticateWithInvalidPasswordAndCollectedException() {
AndFilter filter = new AndFilter();
filter.and(new EqualsFilter("objectclass", "person")).and(new EqualsFilter("uid", "some.person3"));
LdapQuery query = LdapQueryBuilder.query().filter(filter);
assertThatExceptionOfType(AuthenticationException.class).isThrownBy(() ->
tested.authenticate().query(query).password("invalidpassword").execute());
}
@Test
@Category(NoAdTest.class)
public void testAuthenticateWithFilterThatDoesNotMatchAnything() {
AndFilter filter = new AndFilter();
filter.and(new EqualsFilter("objectclass", "person")).and(
new EqualsFilter("uid", "some.person.that.isnt.there"));
LdapQuery query = LdapQueryBuilder.query().filter(filter);
assertThatExceptionOfType(EmptyResultDataAccessException.class).isThrownBy(() ->
tested.authenticate().query(query).password("password").execute());
}
@Test
@Category(NoAdTest.class)
public void testAuthenticateWithFilterThatMatchesSeveralEntries() {
AndFilter filter = new AndFilter();
filter.and(new EqualsFilter("objectclass", "person")).and(new EqualsFilter("cn", "Some Person"));
LdapQuery query = LdapQueryBuilder.query().filter(filter);
assertThatExceptionOfType(IncorrectResultSizeDataAccessException.class).isThrownBy(() ->
tested.authenticate().query(query).password("password").execute());
}
@Test
@Category(NoAdTest.class)
public void testLookupAttemptingCallback() {
AndFilter filter = new AndFilter();
filter.and(new EqualsFilter("objectclass", "person")).and(new EqualsFilter("uid", "some.person3"));
LdapQuery query = LdapQueryBuilder.query().filter(filter);
LookupAttemptingCallback callback = new LookupAttemptingCallback();
tested.authenticate().query(query).password("password").execute(callback);
}
}

View File

@@ -0,0 +1,167 @@
/*
* Copyright 2005-2023 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
*
* https://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.itest;
import javax.naming.directory.Attributes;
import javax.naming.directory.BasicAttribute;
import javax.naming.directory.BasicAttributes;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.ldap.NameNotFoundException;
import org.springframework.ldap.core.DirContextAdapter;
import org.springframework.ldap.core.DirContextOperations;
import org.springframework.ldap.core.LdapClient;
import org.springframework.ldap.support.LdapUtils;
import org.springframework.test.context.ContextConfiguration;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
/**
* Tests the bind and unbind methods of LdapTemplate. The test methods in this
* class tests a little too much, but we need to clean up after binding, so the
* most efficient way to test is to do it all in one test method. Also, the
* methods in this class relies on that the lookup method works as it should -
* that should be ok, since that is verified in a separate test class.
*
* @author Mattias Hellborg Arthursson
*/
@ContextConfiguration(locations = {"/conf/ldapClientTestContext.xml"})
public class DefaultLdapClientBindUnbindITest extends AbstractLdapTemplateIntegrationTest {
@Autowired
private LdapClient tested;
private static final String DN = "cn=Some Person4,ou=company1,ou=Sweden";
@Test
public void testBindAndUnbindWithAttributes() {
Attributes attributes = setupAttributes();
tested.bind(DN).attributes(attributes).execute();
verifyBoundCorrectData();
tested.unbind(DN).execute();
verifyCleanup();
}
@Test
public void testBindGroupOfUniqueNamesWithNameValues() {
DirContextAdapter ctx = new DirContextAdapter(LdapUtils.newLdapName("cn=TEST,ou=groups"));
ctx.addAttributeValue("cn", "TEST");
ctx.addAttributeValue("objectclass", "top");
ctx.addAttributeValue("objectclass", "groupOfUniqueNames");
ctx.addAttributeValue("uniqueMember", LdapUtils.newLdapName("cn=Some Person,ou=company1,ou=Sweden," + base));
tested.bind(ctx.getDn()).object(ctx).execute();
}
@Test
public void testBindAndUnbindWithAttributesUsingLdapName() {
Attributes attributes = setupAttributes();
tested.bind(LdapUtils.newLdapName(DN)).attributes(attributes).execute();
verifyBoundCorrectData();
tested.unbind(LdapUtils.newLdapName(DN)).execute();
verifyCleanup();
}
@Test
public void testBindAndUnbindWithDirContextAdapter() {
DirContextAdapter adapter = new DirContextAdapter();
adapter.setAttributeValues("objectclass", new String[] { "top",
"person" });
adapter.setAttributeValue("cn", "Some Person4");
adapter.setAttributeValue("sn", "Person4");
tested.bind(DN).object(adapter).execute();
verifyBoundCorrectData();
tested.unbind(DN).execute();
verifyCleanup();
}
@Test
public void testBindAndUnbindWithDirContextAdapterUsingLdapName() {
DirContextAdapter adapter = new DirContextAdapter();
adapter.setAttributeValues("objectclass", new String[] { "top",
"person" });
adapter.setAttributeValue("cn", "Some Person4");
adapter.setAttributeValue("sn", "Person4");
tested.bind(LdapUtils.newLdapName(DN)).object(adapter).execute();
verifyBoundCorrectData();
tested.unbind(LdapUtils.newLdapName(DN)).execute();
verifyCleanup();
}
@Test
public void testBindAndUnbindWithDirContextAdapterOnly() {
DirContextAdapter adapter = new DirContextAdapter(LdapUtils.newLdapName(DN));
adapter.setAttributeValues("objectclass", new String[] { "top",
"person" });
adapter.setAttributeValue("cn", "Some Person4");
adapter.setAttributeValue("sn", "Person4");
tested.bind(DN).object(adapter).execute();
verifyBoundCorrectData();
tested.unbind(DN).execute();
verifyCleanup();
}
@Test
public void testBindAndRebindWithDirContextAdapterOnly() {
DirContextAdapter adapter = new DirContextAdapter(LdapUtils.newLdapName(DN));
adapter.setAttributeValues("objectclass", new String[] { "top",
"person" });
adapter.setAttributeValue("cn", "Some Person4");
adapter.setAttributeValue("sn", "Person4");
tested.bind(DN).object(adapter).execute();
verifyBoundCorrectData();
adapter.setAttributeValue("sn", "Person4.Changed");
tested.bind(DN).object(adapter).replaceExisting(true).execute();
verifyReboundCorrectData();
tested.unbind(DN).execute();
verifyCleanup();
}
private Attributes setupAttributes() {
Attributes attributes = new BasicAttributes();
BasicAttribute ocattr = new BasicAttribute("objectclass");
ocattr.add("top");
ocattr.add("person");
attributes.put(ocattr);
attributes.put("cn", "Some Person4");
attributes.put("sn", "Person4");
return attributes;
}
private void verifyBoundCorrectData() {
DirContextOperations result = tested.search().name(DN).toEntry();
assertThat(result.getStringAttribute("cn")).isEqualTo("Some Person4");
assertThat(result.getStringAttribute("sn")).isEqualTo("Person4");
}
private void verifyReboundCorrectData() {
DirContextOperations result = tested.search().name(DN).toEntry();
assertThat(result.getStringAttribute("cn")).isEqualTo("Some Person4");
assertThat(result.getStringAttribute("sn")).isEqualTo("Person4.Changed");
}
private void verifyCleanup() {
assertThatExceptionOfType(NameNotFoundException.class)
.describedAs("NameNotFoundException expected")
.isThrownBy(() -> tested.search().name(DN).toEntry());
}
}

View File

@@ -0,0 +1,176 @@
/*
* Copyright 2005-2023 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
*
* https://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.itest;
import java.util.LinkedList;
import java.util.List;
import javax.naming.NameClassPair;
import javax.naming.ldap.LdapName;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.ldap.core.DirContextAdapter;
import org.springframework.ldap.core.LdapClient;
import org.springframework.ldap.core.support.CountNameClassPairCallbackHandler;
import org.springframework.ldap.support.LdapUtils;
import org.springframework.ldap.test.AttributeCheckContextMapper;
import org.springframework.test.context.ContextConfiguration;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link LdapClient}'s list methods.
*
* @author Josh Cummings
*/
@ContextConfiguration(locations = {"/conf/ldapClientTestContext.xml"})
public class DefaultLdapClientListITest extends AbstractLdapTemplateIntegrationTest {
@Autowired
private LdapClient tested;
private AttributeCheckContextMapper contextMapper;
private static final String BASE_STRING = "";
private static final LdapName BASE_NAME = LdapUtils.newLdapName(BASE_STRING);
private static final String[] ALL_ATTRIBUTES = { "cn", "sn", "description", "telephoneNumber" };
private static final String[] ALL_VALUES = { "Some Person", "Person", "Sweden, Company2, Some Person",
"+46 555-456321" };
@Before
public void prepareTestedInstance() throws Exception {
contextMapper = new AttributeCheckContextMapper();
}
@After
public void tearDown() {
contextMapper = null;
}
@Test
public void testListBindings_ContextMapper() {
contextMapper.setExpectedAttributes(ALL_ATTRIBUTES);
contextMapper.setExpectedValues(ALL_VALUES);
List<DirContextAdapter> list = tested.listBindings("ou=company2,ou=Sweden" + BASE_STRING).toList(contextMapper);
assertThat(list).hasSize(1);
}
@Test
public void testListBindings_ContextMapper_Name() {
contextMapper.setExpectedAttributes(ALL_ATTRIBUTES);
contextMapper.setExpectedValues(ALL_VALUES);
LdapName dn = LdapUtils.newLdapName("ou=company2,ou=Sweden");
List<DirContextAdapter> list = tested.listBindings(dn).toList(contextMapper);
assertThat(list).hasSize(1);
}
@Test
public void testListBindings_ContextMapper_MapToPersons() {
LdapName dn = LdapUtils.newLdapName("ou=company1,ou=Sweden");
List<Person> list = tested.listBindings(dn).toList(new PersonContextMapper());
assertThat(list).hasSize(3);
String personClass = "org.springframework.ldap.itest.Person";
assertThat(list.get(0).getClass().getName()).isEqualTo(personClass);
assertThat(list.get(1).getClass().getName()).isEqualTo(personClass);
assertThat(list.get(2).getClass().getName()).isEqualTo(personClass);
}
@Test
public void testList() {
List<String> list = tested.list(BASE_STRING).toList(NameClassPair::getName);
assertThat(list).hasSize(3);
verifyBindings(list);
}
private void verifyBindings(List<String> list) {
LinkedList<LdapName> transformed = new LinkedList<>();
for (String s : list) {
transformed.add(LdapUtils.newLdapName(s));
}
assertThat(transformed.contains(LdapUtils.newLdapName("ou=groups"))).isTrue();
assertThat(transformed.contains(LdapUtils.newLdapName("ou=Norway"))).isTrue();
assertThat(transformed.contains(LdapUtils.newLdapName("ou=Sweden"))).isTrue();
}
@Test
public void testList_Name() {
List<String> list = tested.list(BASE_NAME).toList(NameClassPair::getName);
assertThat(list).hasSize(3);
verifyBindings(list);
}
@Test
public void testList_Handler() {
CountNameClassPairCallbackHandler handler = new CountNameClassPairCallbackHandler();
tested.list(BASE_STRING).toList((result) -> {
handler.handleNameClassPair(result);
return result;
});
assertThat(handler.getNoOfRows()).isEqualTo(3);
}
@Test
public void testList_Name_Handler() {
CountNameClassPairCallbackHandler handler = new CountNameClassPairCallbackHandler();
tested.list(BASE_NAME).toList((result) -> {
handler.handleNameClassPair(result);
return result;
});
assertThat(handler.getNoOfRows()).isEqualTo(3);
}
@Test
public void testListBindings() {
List<String> list = tested.listBindings(BASE_STRING).toList(NameClassPair::getName);
assertThat(list).hasSize(3);
verifyBindings(list);
}
@Test
public void testListBindings_Name() {
List<String> list = tested.listBindings(BASE_NAME).toList(NameClassPair::getName);
assertThat(list).hasSize(3);
}
@Test
public void testListBindings_Handler() {
CountNameClassPairCallbackHandler handler = new CountNameClassPairCallbackHandler();
tested.list(BASE_STRING).toList((result) -> {
handler.handleNameClassPair(result);
return result;
});
assertThat(handler.getNoOfRows()).isEqualTo(3);
}
@Test
public void testListBindings_Name_Handler() {
CountNameClassPairCallbackHandler handler = new CountNameClassPairCallbackHandler();
tested.list(BASE_NAME).toList((result) -> {
handler.handleNameClassPair(result);
return result;
});
assertThat(handler.getNoOfRows()).isEqualTo(3);
}
}

View File

@@ -0,0 +1,189 @@
/*
* Copyright 2005-2023 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
*
* https://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.itest;
import javax.naming.NamingException;
import javax.naming.directory.Attributes;
import javax.naming.ldap.LdapName;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.ldap.core.AttributesMapper;
import org.springframework.ldap.core.ContextMapper;
import org.springframework.ldap.core.DirContextOperations;
import org.springframework.ldap.core.LdapClient;
import org.springframework.ldap.support.LdapUtils;
import org.springframework.test.context.ContextConfiguration;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link LdapClient}'s lookup methods.
*
* @author Josh Cummings
*/
@ContextConfiguration(locations = {"/conf/ldapClientTestContext.xml"})
public class DefaultLdapClientLookupITest extends AbstractLdapTemplateIntegrationTest {
@Autowired
private LdapClient tested;
/**
* This method depends on a DirObjectFactory (
* {@link org.springframework.ldap.core.support.DefaultDirObjectFactory})
* being set in the ContextSource.
*/
@Test
public void testLookup_Plain() {
DirContextOperations result = tested.search().name("cn=Some Person2, ou=company1,ou=Sweden").toEntry();
assertThat(result.getStringAttribute("cn")).isEqualTo("Some Person2");
assertThat(result.getStringAttribute("sn")).isEqualTo("Person2");
assertThat(result.getStringAttribute("description")).isEqualTo("Sweden, Company1, Some Person2");
}
/**
* This method depends on a DirObjectFactory (
* {@link org.springframework.ldap.core.support.DefaultDirObjectFactory})
* being set in the ContextSource.
*/
@Test
public void testLookupContextRoot() {
DirContextOperations result = tested.search().name("").toEntry();
assertThat(result.getDn().toString()).isEqualTo("");
assertThat(result.getNameInNamespace()).isEqualTo(base);
}
@Test
public void testLookup_AttributesMapper() {
AttributesMapper<Person> mapper = new PersonAttributesMapper();
Person person = tested.search().name("cn=Some Person2, ou=company1,ou=Sweden").toObject(mapper);
assertThat(person.getFullname()).isEqualTo("Some Person2");
assertThat(person.getLastname()).isEqualTo("Person2");
assertThat(person.getDescription()).isEqualTo("Sweden, Company1, Some Person2");
}
@Test
public void testLookup_AttributesMapper_LdapName() {
AttributesMapper<Person> mapper = new PersonAttributesMapper();
Person person = tested.search().name(LdapUtils.newLdapName("cn=Some Person2, ou=company1,ou=Sweden")).toObject(mapper);
assertThat(person.getFullname()).isEqualTo("Some Person2");
assertThat(person.getLastname()).isEqualTo("Person2");
assertThat(person.getDescription()).isEqualTo("Sweden, Company1, Some Person2");
}
/**
* An {@link AttributesMapper} that only maps a subset of the full
* attributes list. Used in tests where the return attributes list has been
* limited.
*
* @author Ulrik Sandberg
*/
private static final class SubsetPersonAttributesMapper implements AttributesMapper<Person> {
/**
* Maps the <code>cn</code> attribute into a {@link Person} object. Also
* verifies that the other attributes haven't been set.
*
* @see AttributesMapper#mapFromAttributes(Attributes)
*/
public Person mapFromAttributes(Attributes attributes) throws NamingException {
Person person = new Person();
person.setFullname((String) attributes.get("cn").get());
assertThat(attributes.get("sn")).as("sn should be null").isNull();
assertThat(attributes.get("description")).as("description should be null").isNull();
return person;
}
}
/**
* Verifies that only the subset is used when specifying a subset of the
* available attributes as return attributes.
*/
@Test
public void testLookup_ReturnAttributes_AttributesMapper() {
AttributesMapper<Person> mapper = new SubsetPersonAttributesMapper();
Person person = tested.search().query((builder) -> builder
.base("cn=Some Person2, ou=company1,ou=Sweden")
.attributes("cn")).toObject(mapper);
assertThat(person.getFullname()).isEqualTo("Some Person2");
assertThat(person.getLastname()).as("lastName should not be set").isNull();
assertThat(person.getDescription()).as("description should not be set").isNull();
}
/**
* Verifies that only the subset is used when specifying a subset of the
* available attributes as return attributes. Uses LdapName instead
* of plain string as name.
*/
@Test
public void testLookup_ReturnAttributes_AttributesMapper_LdapName() {
AttributesMapper<Person> mapper = new SubsetPersonAttributesMapper();
Person person = tested.search().query((builder) -> builder
.base(LdapUtils.newLdapName("cn=Some Person2, ou=company1,ou=Sweden"))
.attributes("cn")).toObject(mapper);
assertThat(person.getFullname()).isEqualTo("Some Person2");
assertThat(person.getLastname()).as("lastName should not be set").isNull();
assertThat(person.getDescription()).as("description should not be set").isNull();
}
/**
* This method depends on a DirObjectFactory (
* {@link org.springframework.ldap.core.support.DefaultDirObjectFactory})
* being set in the ContextSource.
*/
@Test
public void testLookup_ContextMapper() {
ContextMapper<Person> mapper = new PersonContextMapper();
Person person = tested.search().name("cn=Some Person2, ou=company1,ou=Sweden").toObject(mapper);
assertThat(person.getFullname()).isEqualTo("Some Person2");
assertThat(person.getLastname()).isEqualTo("Person2");
assertThat(person.getDescription()).isEqualTo("Sweden, Company1, Some Person2");
}
/**
* Verifies that only the subset is used when specifying a subset of the
* available attributes as return attributes.
*/
@Test
public void testLookup_ReturnAttributes_ContextMapper() {
ContextMapper<Person> mapper = new PersonContextMapper();
Person person = tested.search().query((builder) -> builder
.base("cn=Some Person2, ou=company1,ou=Sweden").attributes("cn")).toObject(mapper);
assertThat(person.getFullname()).isEqualTo("Some Person2");
assertThat(person.getLastname()).as("lastName should not be set").isNull();
assertThat(person.getDescription()).as("description should not be set").isNull();
}
@Test
public void testLookup_GetNameInNamespace_Plain() {
String expectedDn = "cn=Some Person2, ou=company1,ou=Sweden";
DirContextOperations result = tested.search().name(expectedDn).toEntry();
LdapName expectedName = LdapUtils.newLdapName(expectedDn);
assertThat(result.getDn()).isEqualTo(expectedName);
assertThat(result.getNameInNamespace()).isEqualTo("cn=Some Person2,ou=company1,ou=Sweden," + base);
}
}

View File

@@ -0,0 +1,84 @@
/*
* Copyright 2005-2023 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
*
* https://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.itest;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.springframework.LdapDataEntry;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.ldap.core.AttributesMapper;
import org.springframework.ldap.core.DirContextOperations;
import org.springframework.ldap.core.LdapClient;
import org.springframework.test.context.ContextConfiguration;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests {@link LdapClient}'s lookup methods.
*
* @author Josh Cummings
*/
@ContextConfiguration(locations = {"/conf/ldapClientTestContext.xml"})
public class DefaultLdapClientLookupMultiRdnITest extends AbstractLdapTemplateIntegrationTest {
@Autowired
private LdapClient tested;
protected Resource getLdifFileResource() {
return new ClassPathResource("/setup_data_multi_rdn.ldif");
}
/**
* Verifies that we can lookup an entry that has a multi-valued rdn, which
* means more than one attribute is part of the relative DN for the entry.
*/
@Test
@Category(NoAdTest.class)
public void testLookup_MultiValuedRdn() {
AttributesMapper<Person> mapper = new PersonAttributesMapper();
Person person = tested.search().name("cn=Some Person+sn=Person, ou=company1,ou=Norway").toObject(mapper);
assertThat(person.getFullname()).isEqualTo("Some Person");
assertThat(person.getLastname()).isEqualTo("Person");
assertThat(person.getDescription()).isEqualTo("Norway, Company1, Some Person+Person");
}
/**
* Verifies that we can lookup an entry that has a multi-valued rdn, which
* means more than one attribute is part of the relative DN for the entry.
*
*/
@Test
@Category(NoAdTest.class)
public void testLookup_MultiValuedRdn_DirContextAdapter() {
LdapDataEntry result = tested.search().name("cn=Some Person+sn=Person, ou=company1,ou=Norway").toEntry();
assertThat(result.getStringAttribute("cn")).isEqualTo("Some Person");
assertThat(result.getStringAttribute("sn")).isEqualTo("Person");
assertThat(result.getStringAttribute("description")).isEqualTo("Norway, Company1, Some Person+Person");
}
@Test
@Category(NoAdTest.class)
public void testLookup_GetNameInNamespace_MultiRdn() {
DirContextOperations result = tested.search().name("cn=Some Person+sn=Person,ou=company1,ou=Norway").toEntry();
assertThat(result.getDn().toString()).isEqualTo("cn=Some Person+sn=Person,ou=company1,ou=Norway");
assertThat(result.getNameInNamespace()).isEqualTo("cn=Some Person+sn=Person,ou=company1,ou=Norway," + base);
}
}

View File

@@ -0,0 +1,251 @@
/*
* Copyright 2005-2023 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
*
* https://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.itest;
import java.util.Arrays;
import java.util.List;
import javax.naming.directory.Attributes;
import javax.naming.directory.BasicAttribute;
import javax.naming.directory.BasicAttributes;
import javax.naming.directory.DirContext;
import javax.naming.directory.ModificationItem;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.springframework.LdapDataEntry;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.ldap.AttributeInUseException;
import org.springframework.ldap.core.DirContextAdapter;
import org.springframework.ldap.core.DirContextOperations;
import org.springframework.ldap.core.LdapClient;
import org.springframework.ldap.support.LdapUtils;
import org.springframework.test.context.ContextConfiguration;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.fail;
/**
* Tests {@link LdapClient}'s modification methods (rebind and modifyAttributes)
*
* <p>It also illustrates the use of DirContextAdapter as a means of getting
* {@code ModificationItems}, in order to avoid doing a full rebind and use
* {@code modify()} instead.
*
* @author Josh Cummings
*/
@ContextConfiguration(locations = {"/conf/ldapClientTestContext.xml"})
public class DefaultLdapClientModifyITest extends AbstractLdapTemplateIntegrationTest {
@Autowired
private LdapClient tested;
private static final String PERSON4_DN = "cn=Some Person4,ou=company1,ou=Sweden";
private static final String PERSON5_DN = "cn=Some Person5,ou=company1,ou=Sweden";
@Before
public void prepareTestedInstance() throws Exception {
DirContextAdapter adapter = new DirContextAdapter();
adapter.setAttributeValues("objectclass", new String[] { "top", "person" });
adapter.setAttributeValue("cn", "Some Person4");
adapter.setAttributeValue("sn", "Person4");
adapter.setAttributeValue("description", "Some description");
tested.bind(PERSON4_DN).object(adapter).execute();
adapter = new DirContextAdapter();
adapter.setAttributeValues("objectclass", new String[] { "top", "person" });
adapter.setAttributeValue("cn", "Some Person5");
adapter.setAttributeValue("sn", "Person5");
adapter.setAttributeValues("description", new String[] { "qwe", "123", "rty", "uio" });
tested.bind(PERSON5_DN).object(adapter).execute();
}
@After
public void cleanup() throws Exception {
tested.unbind(PERSON4_DN).execute();
tested.unbind(PERSON5_DN).execute();
}
@Test
public void testRebind_Attributes_Plain() {
Attributes attributes = setupAttributes();
tested.bind(PERSON4_DN).attributes(attributes).replaceExisting(true).execute();
verifyBoundCorrectData();
}
@Test
public void testRebind_Attributes_LdapName() {
Attributes attributes = setupAttributes();
tested.bind(LdapUtils.newLdapName(PERSON4_DN)).attributes(attributes).replaceExisting(true).execute();
verifyBoundCorrectData();
}
@Test
public void testModifyAttributes_MultiValueReplace() {
BasicAttribute attr = new BasicAttribute("description", "Some other description");
attr.add("Another description");
ModificationItem[] mods = new ModificationItem[1];
mods[0] = new ModificationItem(DirContext.REPLACE_ATTRIBUTE, attr);
tested.modify(PERSON4_DN).attributes(mods).execute();
DirContextOperations result = tested.search().name(PERSON4_DN).toEntry();
List<String> attributes = Arrays.asList(result.getStringAttributes("description"));
assertThat(attributes).hasSize(2);
assertThat(attributes.contains("Some other description")).isTrue();
assertThat(attributes.contains("Another description")).isTrue();
}
@Test
public void testModifyAttributes_MultiValueAdd() {
BasicAttribute attr = new BasicAttribute("description", "Some other description");
attr.add("Another description");
ModificationItem[] mods = new ModificationItem[1];
mods[0] = new ModificationItem(DirContext.ADD_ATTRIBUTE, attr);
tested.modify(PERSON4_DN).attributes(mods).execute();
LdapDataEntry result = tested.search().name(PERSON4_DN).toEntry();
List<String> attributes = Arrays.asList(result.getStringAttributes("description"));
assertThat(attributes).hasSize(3);
assertThat(attributes.contains("Some other description")).isTrue();
assertThat(attributes.contains("Another description")).isTrue();
assertThat(attributes.contains("Some description")).isTrue();
}
@Test
public void testModifyAttributes_AddAttributeValueWithExistingValue() {
DirContextOperations ctx = tested.search().name("cn=ROLE_USER,ou=groups").toEntry();
String[] existing = ctx.getStringAttributes("uniqueMember");
ctx.addAttributeValue("uniqueMember", "cn=Some Person,ou=company1,ou=Norway," + base);
tested.modify(ctx.getDn()).attributes(ctx.getModificationItems()).execute();
ctx = tested.search().name("cn=ROLE_USER,ou=groups").toEntry();
assertThat(ctx.getStringAttributes("uniqueMember")).hasSize(existing.length + 1);
assertThat(ctx.getStringAttributes("uniqueMember")).contains("cn=Some Person,ou=company1,ou=Norway," + base);
}
@Test
public void testModifyAttributes_MultiValueAddDuplicateToUnordered() {
BasicAttribute attr = new BasicAttribute("description", "Some description");
ModificationItem[] mods = new ModificationItem[1];
mods[0] = new ModificationItem(DirContext.ADD_ATTRIBUTE, attr);
try {
tested.modify(PERSON4_DN).attributes(mods).execute();
fail("AttributeInUseException expected");
}
catch (AttributeInUseException expected) {
// expected
}
}
/**
* Test written originally to verify that duplicates are allowed on ordered
* attributes, but had to be changed since Apache DS seems to disallow
* duplicates even for ordered attributes.
*/
@Test
public void testModifyAttributes_MultiValueAddDuplicateToOrdered() {
BasicAttribute attr = new BasicAttribute("description", "Some other description", true); // ordered
attr.add("Another description");
// Commented out duplicate to make test work for Apache DS
// attr.add("Some description");
ModificationItem[] mods = new ModificationItem[1];
mods[0] = new ModificationItem(DirContext.ADD_ATTRIBUTE, attr);
tested.modify(PERSON4_DN).attributes(mods).execute();
LdapDataEntry result = tested.search().name(PERSON4_DN).toEntry();
List<String> attributes = Arrays.asList(result.getStringAttributes("description"));
assertThat(attributes).hasSize(3);
assertThat(attributes.contains("Some other description")).isTrue();
assertThat(attributes.contains("Another description")).isTrue();
assertThat(attributes.contains("Some description")).isTrue();
}
@Test
public void testModifyAttributes_Plain() {
ModificationItem item = new ModificationItem(DirContext.REPLACE_ATTRIBUTE, new BasicAttribute("description",
"Some other description"));
tested.modify(PERSON4_DN).attributes(item).execute();
verifyBoundCorrectData();
}
@Test
public void testModifyAttributes_LdapName() {
ModificationItem item = new ModificationItem(DirContext.REPLACE_ATTRIBUTE, new BasicAttribute("description",
"Some other description"));
tested.modify(LdapUtils.newLdapName(PERSON4_DN)).attributes(item).execute();
verifyBoundCorrectData();
}
@Test
public void testModifyAttributes_DirContextAdapter_MultiAttributes() {
DirContextOperations adapter = tested.search().name(PERSON5_DN).toEntry();
adapter.setAttributeValues("description", new String[] { "qwe", "123", "klytt", "kalle" });
tested.modify(adapter.getDn()).attributes(adapter.getModificationItems()).execute();
// Verify
adapter = tested.search().name(PERSON5_DN).toEntry();
List<String> attributes = Arrays.asList(adapter.getStringAttributes("description"));
assertThat(attributes).hasSize(4);
assertThat(attributes.contains("qwe")).isTrue();
assertThat(attributes.contains("123")).isTrue();
assertThat(attributes.contains("klytt")).isTrue();
assertThat(attributes.contains("kalle")).isTrue();
}
/**
* Demonstrates how the DirContextAdapter can be used to automatically keep
* track of changes of the attributes and deliver ModificationItems to use
* in moifyAttributes().
*/
@Test
public void testModifyAttributes_DirContextAdapter() {
DirContextOperations adapter = tested.search().name(PERSON4_DN).toEntry();
adapter.setAttributeValue("description", "Some other description");
tested.modify(adapter.getDn()).attributes(adapter.getModificationItems()).execute();
verifyBoundCorrectData();
}
@Test
public void verifyCompleteReplacementOfUniqueMemberAttribute_Ldap119() {
DirContextOperations ctx = tested.search().name("cn=ROLE_USER,ou=groups").toEntry();
ctx.setAttributeValues("uniqueMember", new String[] { "cn=Some Person,ou=company1,ou=Norway," + base }, true);
tested.modify(ctx.getDn()).attributes(ctx.getModificationItems()).execute();
}
private Attributes setupAttributes() {
Attributes attributes = new BasicAttributes();
BasicAttribute ocattr = new BasicAttribute("objectclass");
ocattr.add("top");
ocattr.add("person");
attributes.put(ocattr);
attributes.put("cn", "Some Person4");
attributes.put("sn", "Person4");
attributes.put("description", "Some other description");
return attributes;
}
private void verifyBoundCorrectData() {
DirContextOperations result = tested.search().name(PERSON4_DN).toEntry();
assertThat(result.getStringAttribute("cn")).isEqualTo("Some Person4");
assertThat(result.getStringAttribute("sn")).isEqualTo("Person4");
assertThat(result.getStringAttribute("description")).isEqualTo("Some other description");
}
}

View File

@@ -0,0 +1,129 @@
/*
* Copyright 2005-2023 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
*
* https://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.itest;
import javax.naming.Name;
import javax.naming.NameClassPair;
import javax.naming.ldap.LdapName;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.ldap.NameNotFoundException;
import org.springframework.ldap.core.DirContextAdapter;
import org.springframework.ldap.core.LdapClient;
import org.springframework.ldap.support.LdapUtils;
import org.springframework.test.context.ContextConfiguration;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
/**
* Tests {@code LdapClient}'s recursive modification methods (unbind and the protected delete
* methods).
*
* @author Josh Cummings
*/
@ContextConfiguration(locations = {"/conf/ldapClientTestContext.xml"})
public class DefaultLdapClientRecursiveDeleteITest extends AbstractLdapTemplateIntegrationTest {
@Autowired
private LdapClient tested;
private static final LdapName DN = LdapUtils.newLdapName("cn=Some Person5,ou=company1,ou=Sweden");
private LdapName firstSubDn;
private LdapName secondSubDn;
private LdapName leafDn;
@Before
public void prepareTestedInstance() throws Exception {
DirContextAdapter adapter = new DirContextAdapter();
adapter.setAttributeValues("objectclass", new String[] { "top", "person" });
adapter.setAttributeValue("cn", "Some Person5");
adapter.setAttributeValue("sn", "Person5");
adapter.setAttributeValue("description", "Some description");
tested.bind(DN).object(adapter).execute();
firstSubDn = LdapUtils.newLdapName("cn=subPerson");
firstSubDn = LdapUtils.prepend(firstSubDn, DN);
adapter = new DirContextAdapter();
adapter.setAttributeValues("objectclass", new String[] { "top", "person" });
adapter.setAttributeValue("cn", "subPerson");
adapter.setAttributeValue("sn", "subPerson");
adapter.setAttributeValue("description", "Should be recursively deleted");
tested.bind(firstSubDn).object(adapter).execute();
secondSubDn = LdapUtils.newLdapName("cn=subPerson2");
secondSubDn = LdapUtils.prepend(secondSubDn, DN);
adapter = new DirContextAdapter();
adapter.setAttributeValues("objectclass", new String[] { "top", "person" });
adapter.setAttributeValue("cn", "subPerson2");
adapter.setAttributeValue("sn", "subPerson2");
adapter.setAttributeValue("description", "Should be recursively deleted");
tested.bind(secondSubDn).object(adapter).execute();
leafDn = LdapUtils.newLdapName("cn=subSubPerson");
leafDn = LdapUtils.prepend(leafDn, DN);
adapter = new DirContextAdapter();
adapter.setAttributeValues("objectclass", new String[] { "top", "person" });
adapter.setAttributeValue("cn", "subSubPerson");
adapter.setAttributeValue("sn", "subSubPerson");
adapter.setAttributeValue("description", "Should be recursively deleted");
tested.bind(leafDn).object(adapter).execute();
}
@After
public void cleanup() throws Exception {
try {
tested.unbind(DN).recursive(true).execute();
}
catch (NameNotFoundException ignore) {
// ignore
}
}
@Test
@Category(NoAdTest.class)
public void testRecursiveUnbind() {
tested.unbind(DN).recursive(true).execute();
verifyDeleted(DN);
verifyDeleted(firstSubDn);
verifyDeleted(secondSubDn);
verifyDeleted(leafDn);
}
@Test
@Category(NoAdTest.class)
public void testRecursiveUnbindOnLeaf() {
tested.unbind(leafDn).recursive(true).execute();
verifyDeleted(leafDn);
}
private void verifyDeleted(Name dn) {
assertThatExceptionOfType(NameNotFoundException.class)
.describedAs("Expected entry '" + dn + "' to be non-existent")
.isThrownBy(() -> tested.list(dn).toList(NameClassPair::getName));
}
}

View File

@@ -0,0 +1,102 @@
/*
* Copyright 2005-2023 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
*
* https://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.itest;
import javax.naming.Name;
import javax.naming.NameClassPair;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.springframework.LdapDataEntry;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.ldap.NameNotFoundException;
import org.springframework.ldap.core.DirContextAdapter;
import org.springframework.ldap.core.DirContextOperations;
import org.springframework.ldap.core.LdapClient;
import org.springframework.ldap.support.LdapUtils;
import org.springframework.test.context.ContextConfiguration;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.fail;
/**
* Tests {@link LdapClient}'s rename methods.
*
* @author Josh Cummings
*/
@ContextConfiguration(locations = {"/conf/ldapClientTestContext.xml"})
public class DefaultLdapClientRenameITest extends AbstractLdapTemplateIntegrationTest {
@Autowired
private LdapClient tested;
private static final String DN = "cn=Some Person6,ou=company1,ou=Sweden";
private static final String NEWDN = "cn=Some Person6,ou=company2,ou=Sweden";
@Before
public void prepareTestedInstance() throws Exception {
DirContextAdapter adapter = new DirContextAdapter();
adapter.setAttributeValues("objectclass", new String[] { "top", "person" });
adapter.setAttributeValue("cn", "Some Person6");
adapter.setAttributeValue("sn", "Person6");
adapter.setAttributeValue("description", "Some description");
tested.bind(DN).object(adapter).execute();
}
@After
public void cleanup() throws Exception {
tested.unbind(NEWDN).execute();
tested.unbind(DN).execute();
}
@Test
public void testRename() {
tested.modify(DN).name(NEWDN).execute();
verifyDeleted(LdapUtils.newLdapName(DN));
verifyBoundCorrectData();
}
@Test
public void testRename_LdapName() {
Name oldDn = LdapUtils.newLdapName(DN);
Name newDn = LdapUtils.newLdapName(NEWDN);
tested.modify(oldDn).name(newDn).execute();
verifyDeleted(oldDn);
verifyBoundCorrectData();
}
private void verifyDeleted(Name dn) {
try {
tested.list(dn).toList(NameClassPair::getName);
fail("Expected entry '" + dn + "' to be non-existent");
}
catch (NameNotFoundException expected) {
// expected
}
}
private void verifyBoundCorrectData() {
LdapDataEntry result = tested.search().name(NEWDN).toEntry();
assertThat(result.getStringAttribute("cn")).isEqualTo("Some Person6");
assertThat(result.getStringAttribute("sn")).isEqualTo("Person6");
assertThat(result.getStringAttribute("description")).isEqualTo("Some description");
}
}

View File

@@ -0,0 +1,543 @@
/*
* Copyright 2005-2023 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
*
* https://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.itest;
import java.util.List;
import java.util.stream.Collectors;
import javax.naming.Name;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.IncorrectResultSizeDataAccessException;
import org.springframework.ldap.NameNotFoundException;
import org.springframework.ldap.SizeLimitExceededException;
import org.springframework.ldap.core.ContextMapper;
import org.springframework.ldap.core.DirContextAdapter;
import org.springframework.ldap.core.DirContextOperations;
import org.springframework.ldap.core.LdapClient;
import org.springframework.ldap.query.LdapQuery;
import org.springframework.ldap.query.LdapQueryBuilder;
import org.springframework.ldap.query.SearchScope;
import org.springframework.ldap.support.LdapUtils;
import org.springframework.ldap.test.AttributeCheckAttributesMapper;
import org.springframework.ldap.test.AttributeCheckContextMapper;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.annotation.DirtiesContext.ClassMode;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.util.ReflectionTestUtils;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.fail;
import static org.springframework.ldap.query.LdapQueryBuilder.query;
/**
* Tests for {@link LdapClient}'s search methods.
*
* @author Josh Cummings
*/
@ContextConfiguration(locations = {"/conf/ldapClientTestContext.xml"})
@DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD)
public class DefaultLdapClientSearchResultITest extends AbstractLdapTemplateIntegrationTest {
@Autowired
private LdapClient tested;
private AttributeCheckAttributesMapper attributesMapper;
private AttributeCheckContextMapper contextMapper;
private static final String[] ALL_ATTRIBUTES = { "cn", "sn", "description", "telephoneNumber" };
private static final String[] CN_SN_ATTRS = { "cn", "sn" };
private static final String[] ABSENT_ATTRIBUTES = { "description", "telephoneNumber" };
private static final String[] CN_SN_VALUES = { "Some Person2", "Person2" };
private static final String[] ALL_VALUES = { "Some Person2", "Person2", "Sweden, Company1, Some Person2",
"+46 555-654321" };
private static final String BASE_STRING = "";
private static final String FILTER_STRING = "(&(objectclass=person)(sn=Person2))";
private static final Name BASE_NAME = LdapUtils.newLdapName(BASE_STRING);
@Before
public void prepareTestedInstance() throws Exception {
attributesMapper = new AttributeCheckAttributesMapper();
contextMapper = new AttributeCheckContextMapper();
}
@After
public void cleanup() throws Exception {
attributesMapper = null;
contextMapper = null;
}
@Test
public void testSearch_AttributesMapper() {
attributesMapper.setExpectedAttributes(ALL_ATTRIBUTES);
attributesMapper.setExpectedValues(ALL_VALUES);
LdapQuery query = LdapQueryBuilder.query().base(BASE_STRING).filter(FILTER_STRING);
List<Object> list = tested.search().query(query).toList(attributesMapper);
assertThat(list).hasSize(1);
}
@Test
public void testSearch_LdapQuery_AttributesMapper() {
attributesMapper.setExpectedAttributes(ALL_ATTRIBUTES);
attributesMapper.setExpectedValues(ALL_VALUES);
List<Object> list = tested.search().query(query()
.base(BASE_STRING)
.where("objectclass").is("person").and("sn").is("Person2"))
.toList(attributesMapper);
assertThat(list).hasSize(1);
}
@Test
public void testSearchForStream_LdapQuery_AttributesMapper() {
attributesMapper.setExpectedAttributes(ALL_ATTRIBUTES);
attributesMapper.setExpectedValues(ALL_VALUES);
List<Object> list = tested.search().query(query()
.base(BASE_STRING)
.where("objectclass").is("person").and("sn").is("Person2"))
.toStream(attributesMapper).collect(Collectors.toList());
assertThat(list).hasSize(1);
}
@Test
public void testSearch_LdapQuery_AttributesMapper_FewerAttributes() {
attributesMapper.setExpectedAttributes(new String[] {"cn"});
attributesMapper.setExpectedValues(new String[]{"Some Person2"});
List<Object> list = tested.search().query(query()
.base(BASE_STRING)
.attributes("cn")
.where("objectclass").is("person").and("sn").is("Person2"))
.toList(attributesMapper);
assertThat(list).hasSize(1);
}
@Test
public void testSearchForStream_LdapQuery_AttributesMapper_FewerAttributes() {
attributesMapper.setExpectedAttributes(new String[] {"cn"});
attributesMapper.setExpectedValues(new String[]{"Some Person2"});
List<Object> list = tested.search().query(query()
.base(BASE_STRING)
.attributes("cn")
.where("objectclass").is("person").and("sn").is("Person2"))
.toStream(attributesMapper).collect(Collectors.toList());
assertThat(list).hasSize(1);
}
@Test
public void testSearch_LdapQuery_AttributesMapper_SearchScope() {
attributesMapper.setExpectedAttributes(ALL_ATTRIBUTES);
attributesMapper.setExpectedValues(ALL_VALUES);
List<Object> list = tested.search().query(query()
.base(BASE_STRING)
.searchScope(SearchScope.ONELEVEL)
.where("objectclass").is("person").and("sn").is("Person2"))
.toList(attributesMapper);
assertThat(list).isEmpty();
}
@Test
public void testSearchForStream_LdapQuery_AttributesMapper_SearchScope() {
attributesMapper.setExpectedAttributes(ALL_ATTRIBUTES);
attributesMapper.setExpectedValues(ALL_VALUES);
List<Object> list = tested.search().query(query()
.base(BASE_STRING)
.searchScope(SearchScope.ONELEVEL)
.where("objectclass").is("person").and("sn").is("Person2"))
.toStream(attributesMapper).collect(Collectors.toList());
assertThat(list).isEmpty();
}
@Test
public void testSearch_LdapQuery_AttributesMapper_SearchScope_CorrectBase() {
attributesMapper.setExpectedAttributes(ALL_ATTRIBUTES);
attributesMapper.setExpectedValues(ALL_VALUES);
List<Object> list = tested.search().query(query()
.base("ou=company1,ou=Sweden")
.searchScope(SearchScope.ONELEVEL)
.where("objectclass").is("person").and("sn").is("Person2"))
.toList(attributesMapper);
assertThat(list).hasSize(1);
}
@Test
public void testSearchForStream_LdapQuery_AttributesMapper_SearchScope_CorrectBase() {
attributesMapper.setExpectedAttributes(ALL_ATTRIBUTES);
attributesMapper.setExpectedValues(ALL_VALUES);
List<Object> list = tested.search().query(query()
.base("ou=company1,ou=Sweden")
.searchScope(SearchScope.ONELEVEL)
.where("objectclass").is("person").and("sn").is("Person2"))
.toStream(attributesMapper).collect(Collectors.toList());
assertThat(list).hasSize(1);
}
@Test
public void testSearch_LdapQuery_AttributesMapper_NoBase() {
attributesMapper.setExpectedAttributes(ALL_ATTRIBUTES);
attributesMapper.setExpectedValues(ALL_VALUES);
List<Object> list = tested.search().query(query()
.where("objectclass").is("person").and("sn").is("Person2"))
.toList(attributesMapper);
assertThat(list).hasSize(1);
}
@Test
public void testSearchForStream_LdapQuery_AttributesMapper_NoBase() {
attributesMapper.setExpectedAttributes(ALL_ATTRIBUTES);
attributesMapper.setExpectedValues(ALL_VALUES);
List<Object> list = tested.search().query(query()
.where("objectclass").is("person").and("sn").is("Person2"))
.toStream(attributesMapper).collect(Collectors.toList());
assertThat(list).hasSize(1);
}
@Test
public void testSearch_LdapQuery_AttributesMapper_DifferentBase() {
attributesMapper.setExpectedAttributes(ALL_ATTRIBUTES);
attributesMapper.setExpectedValues(ALL_VALUES);
List<Object> list = tested.search().query(query()
.base("ou=Norway")
.where("objectclass").is("person").and("sn").is("Person2"))
.toList(attributesMapper);
assertThat(list).isEmpty();
}
@Test
public void testSearchForStream_LdapQuery_AttributesMapper_DifferentBase() {
attributesMapper.setExpectedAttributes(ALL_ATTRIBUTES);
attributesMapper.setExpectedValues(ALL_VALUES);
List<Object> list = tested.search().query(query()
.base("ou=Norway")
.where("objectclass").is("person").and("sn").is("Person2"))
.toStream(attributesMapper).collect(Collectors.toList());
assertThat(list).isEmpty();
}
@Test
public void testSearch_SearchScope_AttributesMapper() {
attributesMapper.setExpectedAttributes(ALL_ATTRIBUTES);
attributesMapper.setExpectedValues(ALL_VALUES);
List<Object> list = tested.search().query(query().base(BASE_STRING)
.searchScope(SearchScope.SUBTREE).filter(FILTER_STRING))
.toList(attributesMapper);
assertThat(list).hasSize(1);
}
@Test
public void testSearch_SearchScope_LimitedAttrs_AttributesMapper() {
attributesMapper.setExpectedAttributes(CN_SN_ATTRS);
attributesMapper.setExpectedValues(CN_SN_VALUES);
attributesMapper.setAbsentAttributes(ABSENT_ATTRIBUTES);
List<Object> list = tested.search().query(query().base(BASE_STRING)
.searchScope(SearchScope.SUBTREE)
.attributes(CN_SN_ATTRS)
.filter(FILTER_STRING))
.toList(attributesMapper);
assertThat(list).hasSize(1);
}
@Test
public void testSearch_AttributesMapper_Name() {
attributesMapper.setExpectedAttributes(ALL_ATTRIBUTES);
attributesMapper.setExpectedValues(ALL_VALUES);
List<Object> list = tested.search().query(query().base(BASE_NAME).filter(FILTER_STRING))
.toList(attributesMapper);
assertThat(list).hasSize(1);
}
@Test
public void testSearch_SearchScope_AttributesMapper_Name() {
attributesMapper.setExpectedAttributes(ALL_ATTRIBUTES);
attributesMapper.setExpectedValues(ALL_VALUES);
List<Object> list = tested.search().query(query().base(BASE_NAME).searchScope(SearchScope.SUBTREE)
.filter(FILTER_STRING)).toList(attributesMapper);
assertThat(list).hasSize(1);
}
@Test
public void testSearch_SearchScope_LimitedAttrs_AttributesMapper_Name() {
attributesMapper.setExpectedAttributes(CN_SN_ATTRS);
attributesMapper.setExpectedValues(CN_SN_VALUES);
attributesMapper.setAbsentAttributes(ABSENT_ATTRIBUTES);
List<Object> list = tested.search().query(query().base(BASE_NAME).searchScope(SearchScope.SUBTREE)
.attributes(CN_SN_ATTRS).filter(FILTER_STRING)).toList(attributesMapper);
assertThat(list).hasSize(1);
}
@Test
public void testSearch_ContextMapper() {
contextMapper.setExpectedAttributes(ALL_ATTRIBUTES);
contextMapper.setExpectedValues(ALL_VALUES);
List<DirContextAdapter> list = tested.search().query(query().base(BASE_STRING).filter(FILTER_STRING))
.toList(contextMapper);
assertThat(list).hasSize(1);
}
@Test
public void testSearchForObject() {
contextMapper.setExpectedAttributes(ALL_ATTRIBUTES);
contextMapper.setExpectedValues(ALL_VALUES);
DirContextAdapter result = tested.search().query(query().base(BASE_STRING).filter(FILTER_STRING))
.toObject(contextMapper);
assertThat(result).isNotNull();
}
@Test(expected = IncorrectResultSizeDataAccessException.class)
public void testSearchForObjectWithMultipleHits() {
tested.search().query(query().base(BASE_STRING).filter("(&(objectclass=person)(sn=*))"))
.toObject((Object ctx) -> ctx);
}
@Test//(expected = EmptyResultDataAccessException.class)
public void testSearchForObjectNoHits() {
Object result = tested.search().query(query().base(BASE_STRING)
.filter("(&(objectclass=person)(sn=Person does not exist))"))
.toObject((Object ctx) -> ctx);
assertThat(result).isNull();
}
@Test
public void testSearch_SearchScope_ContextMapper() {
contextMapper.setExpectedAttributes(ALL_ATTRIBUTES);
contextMapper.setExpectedValues(ALL_VALUES);
List<DirContextAdapter> list = tested.search().query(query().base(BASE_STRING).searchScope(SearchScope.SUBTREE)
.filter(FILTER_STRING)).toList(contextMapper);
assertThat(list).hasSize(1);
}
@Test
public void testSearch_SearchScope_LimitedAttrs_ContextMapper() {
contextMapper.setExpectedAttributes(CN_SN_ATTRS);
contextMapper.setExpectedValues(CN_SN_VALUES);
contextMapper.setAbsentAttributes(ABSENT_ATTRIBUTES);
List<DirContextAdapter> list = tested.search().query(query().base(BASE_STRING).searchScope(SearchScope.SUBTREE)
.attributes(CN_SN_ATTRS).filter(FILTER_STRING)).toList(contextMapper);
assertThat(list).hasSize(1);
}
@Test
public void testSearch_ContextMapper_Name() {
contextMapper.setExpectedAttributes(ALL_ATTRIBUTES);
contextMapper.setExpectedValues(ALL_VALUES);
List<DirContextAdapter> list = tested.search().query(query().base(BASE_NAME).filter(FILTER_STRING))
.toList(contextMapper);
assertThat(list).hasSize(1);
}
@Test
public void testSearch_ContextMapper_LdapQuery() {
contextMapper.setExpectedAttributes(ALL_ATTRIBUTES);
contextMapper.setExpectedValues(ALL_VALUES);
List<DirContextAdapter> list = tested.search().query(query()
.base(BASE_NAME)
.where("objectclass").is("person").and("sn").is("Person2"))
.toList(contextMapper);
assertThat(list).hasSize(1);
}
@Test
public void testSearchForStream_ContextMapper_LdapQuery() {
contextMapper.setExpectedAttributes(ALL_ATTRIBUTES);
contextMapper.setExpectedValues(ALL_VALUES);
List<DirContextAdapter> list = tested.search().query(query()
.base(BASE_NAME)
.where("objectclass").is("person").and("sn").is("Person2"))
.toStream(contextMapper).collect(Collectors.toList());
assertThat(list).hasSize(1);
}
@Test
public void testSearch_ContextMapper_LdapQuery_NoBase() {
contextMapper.setExpectedAttributes(ALL_ATTRIBUTES);
contextMapper.setExpectedValues(ALL_VALUES);
List<DirContextAdapter> list = tested.search().query(query()
.where("objectclass").is("person").and("sn").is("Person2"))
.toList(contextMapper);
assertThat(list).hasSize(1);
}
@Test
public void testSearchForStream_ContextMapper_LdapQuery_NoBase() {
contextMapper.setExpectedAttributes(ALL_ATTRIBUTES);
contextMapper.setExpectedValues(ALL_VALUES);
List<DirContextAdapter> list = tested.search().query(query()
.where("objectclass").is("person").and("sn").is("Person2"))
.toStream(contextMapper).collect(Collectors.toList());
assertThat(list).hasSize(1);
}
@Test
public void testSearch_ContextMapper_LdapQuery_SearchScope() {
contextMapper.setExpectedAttributes(ALL_ATTRIBUTES);
contextMapper.setExpectedValues(ALL_VALUES);
List<DirContextAdapter> list = tested.search().query(query()
.base(BASE_NAME)
.searchScope(SearchScope.ONELEVEL)
.where("objectclass").is("person").and("sn").is("Person2"))
.toList(contextMapper);
assertThat(list).isEmpty();
}
@Test
public void testSearchForStream_ContextMapper_LdapQuery_SearchScope() {
contextMapper.setExpectedAttributes(ALL_ATTRIBUTES);
contextMapper.setExpectedValues(ALL_VALUES);
List<DirContextAdapter> list = tested.search().query(query()
.base(BASE_NAME)
.searchScope(SearchScope.ONELEVEL)
.where("objectclass").is("person").and("sn").is("Person2"))
.toStream(contextMapper).collect(Collectors.toList());
assertThat(list).isEmpty();
}
@Test
public void testSearch_ContextMapper_LdapQuery_SearchScope_CorrectBase() {
contextMapper.setExpectedAttributes(ALL_ATTRIBUTES);
contextMapper.setExpectedValues(ALL_VALUES);
List<DirContextAdapter> list = tested.search().query(query()
.base("ou=company1,ou=Sweden")
.searchScope(SearchScope.ONELEVEL)
.where("objectclass").is("person").and("sn").is("Person2"))
.toList(contextMapper);
assertThat(list).hasSize(1);
}
@Test
public void testSearchForStream_ContextMapper_LdapQuery_SearchScope_CorrectBase() {
contextMapper.setExpectedAttributes(ALL_ATTRIBUTES);
contextMapper.setExpectedValues(ALL_VALUES);
List<DirContextAdapter> list = tested.search().query(query()
.base("ou=company1,ou=Sweden")
.searchScope(SearchScope.ONELEVEL)
.where("objectclass").is("person").and("sn").is("Person2"))
.toStream(contextMapper).collect(Collectors.toList());
assertThat(list).hasSize(1);
}
@Test
public void testSearchForContext_LdapQuery() {
ContextMapper<DirContextOperations> mapper = (result) -> (DirContextOperations) result;
DirContextOperations result = tested.search().query(query()
.where("objectclass").is("person").and("sn").is("Person2")).toObject(mapper);
assertThat(result).isNotNull();
assertThat(result.getStringAttribute("sn")).isEqualTo("Person2");
}
@Test//(expected = EmptyResultDataAccessException.class)
public void testSearchForContext_LdapQuery_SearchScopeNotFound() {
Object result = tested.search().query(query()
.searchScope(SearchScope.ONELEVEL)
.where("objectclass").is("person").and("sn").is("Person2")).toObject(attributesMapper);
assertThat(result).isNull();
}
@Test
public void testSearchForContext_LdapQuery_SearchScope_CorrectBase() {
ContextMapper<DirContextOperations> mapper = (result) -> (DirContextOperations) result;
DirContextOperations result =
tested.search().query(query()
.searchScope(SearchScope.ONELEVEL)
.base("ou=company1,ou=Sweden")
.where("objectclass").is("person").and("sn").is("Person2")).toObject(mapper);
assertThat(result).isNotNull();
assertThat(result.getStringAttribute("sn")).isEqualTo("Person2");
}
@Test
public void testSearch_SearchScope_ContextMapper_Name() {
contextMapper.setExpectedAttributes(ALL_ATTRIBUTES);
contextMapper.setExpectedValues(ALL_VALUES);
List<DirContextAdapter> list = tested.search().query(query().base(BASE_NAME).searchScope(SearchScope.SUBTREE)
.filter(FILTER_STRING)).toList(contextMapper);
assertThat(list).hasSize(1);
}
@Test
public void testSearch_SearchScope_LimitedAttrs_ContextMapper_Name() {
contextMapper.setExpectedAttributes(CN_SN_ATTRS);
contextMapper.setExpectedValues(CN_SN_VALUES);
contextMapper.setAbsentAttributes(ABSENT_ATTRIBUTES);
List<DirContextAdapter> list = tested.search().query(query().base(BASE_NAME).searchScope(SearchScope.SUBTREE)
.attributes(CN_SN_ATTRS).filter(FILTER_STRING)).toList(contextMapper);
assertThat(list).hasSize(1);
}
@Test
public void testSearchWithInvalidSearchBaseShouldByDefaultThrowException() {
try {
tested.search().query(query().base(BASE_NAME + "ou=unknown").searchScope(SearchScope.SUBTREE)
.attributes(CN_SN_ATTRS).filter(FILTER_STRING))
.toObject(contextMapper);
fail("NameNotFoundException expected");
}
catch (NameNotFoundException expected) {
assertThat(true).isTrue();
}
}
@Test
public void testSearchWithInvalidSearchBaseCanBeConfiguredToSwallowException() {
ReflectionTestUtils.setField(tested, "ignoreNameNotFoundException", true);
contextMapper.setExpectedAttributes(CN_SN_ATTRS);
contextMapper.setExpectedValues(CN_SN_VALUES);
contextMapper.setAbsentAttributes(ABSENT_ATTRIBUTES);
List<DirContextAdapter> list = tested.search().query(query().base(BASE_NAME + "ou=unknown")
.searchScope(SearchScope.SUBTREE).attributes(CN_SN_ATTRS).filter(FILTER_STRING))
.toList(contextMapper);
assertThat(list).isEmpty();
}
@Test
public void verifyThatSearchWithCountLimitReturnsTheEntriesFoundSoFar() {
List<Object> result = tested.search().query(query()
.countLimit(3)
.where("objectclass").is("person")).toList((Object ctx) -> new Object());
assertThat(result).hasSize(3);
}
@Test(expected = SizeLimitExceededException.class)
public void verifyThatSearchWithCountLimitWithFlagToFalseThrowsException() {
ReflectionTestUtils.setField(tested, "ignoreSizeLimitExceededException", false);
tested.search().query(query()
.countLimit(3)
.where("objectclass").is("person")).toList((Object ctx) -> ctx);
}
}

View File

@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<import resource="classpath:/conf/commonTestContext.xml" />
<import resource="classpath:/conf/commonContextSourceConfig.xml"/>
<bean id="ldapClient"
class="org.springframework.ldap.core.LdapClient" factory-method="create">
<constructor-arg ref="contextSource" />
</bean>
</beans>