Added support for authenticate, modify_attributes and function_driven operations
Project: http://git-wip-us.apache.org/repos/asf/camel/repo Commit: http://git-wip-us.apache.org/repos/asf/camel/commit/1195acaa Tree: http://git-wip-us.apache.org/repos/asf/camel/tree/1195acaa Diff: http://git-wip-us.apache.org/repos/asf/camel/diff/1195acaa Branch: refs/heads/master Commit: 1195acaa2b823fb8f97fa0fe3198ebffaba2f772 Parents: 77f03fa Author: Bhavesh Thakker <[email protected]> Authored: Wed Nov 16 15:59:45 2016 -0500 Committer: Andrea Cosentino <[email protected]> Committed: Fri Nov 18 08:53:26 2016 +0100 ---------------------------------------------------------------------- .../component/springldap/LdapOperation.java | 10 +- .../springldap/LdapOperationsFunction.java | 24 +++++ .../springldap/SpringLdapProducer.java | 62 +++++++---- .../springldap/SpringLdapProducerTest.java | 108 ++++++++++++++++--- 4 files changed, 166 insertions(+), 38 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/camel/blob/1195acaa/components/camel-spring-ldap/src/main/java/org/apache/camel/component/springldap/LdapOperation.java ---------------------------------------------------------------------- diff --git a/components/camel-spring-ldap/src/main/java/org/apache/camel/component/springldap/LdapOperation.java b/components/camel-spring-ldap/src/main/java/org/apache/camel/component/springldap/LdapOperation.java index 2710acc..68e219e 100644 --- a/components/camel-spring-ldap/src/main/java/org/apache/camel/component/springldap/LdapOperation.java +++ b/components/camel-spring-ldap/src/main/java/org/apache/camel/component/springldap/LdapOperation.java @@ -16,10 +16,14 @@ */ package org.apache.camel.component.springldap; +import org.springframework.ldap.core.LdapOperations; + /** - * The list of supported LDAP operations. Currently supported operations are - * search, bind, and unbind. Modify is currently not supported. + * The list of supported LDAP operations. Currently supported operations are search, bind, and unbind, authenticate and modify_attributes. + * + * The function_driven operation expects a request {@link Object} along with an instance of {@link LdapOperationsFunction} that can be used to invoke any method on the + * {@link LdapOperations} instance */ public enum LdapOperation { - SEARCH, BIND, UNBIND + SEARCH, BIND, UNBIND, AUTHENTICATE, MODIFY_ATTRIBUTES, FUNCTION_DRIVEN } http://git-wip-us.apache.org/repos/asf/camel/blob/1195acaa/components/camel-spring-ldap/src/main/java/org/apache/camel/component/springldap/LdapOperationsFunction.java ---------------------------------------------------------------------- diff --git a/components/camel-spring-ldap/src/main/java/org/apache/camel/component/springldap/LdapOperationsFunction.java b/components/camel-spring-ldap/src/main/java/org/apache/camel/component/springldap/LdapOperationsFunction.java new file mode 100644 index 0000000..403a80c --- /dev/null +++ b/components/camel-spring-ldap/src/main/java/org/apache/camel/component/springldap/LdapOperationsFunction.java @@ -0,0 +1,24 @@ +package org.apache.camel.component.springldap; + +import org.springframework.ldap.core.LdapOperations; + +/** + * Provides a way to invoke any method on {@link LdapOperations} when an operation is not provided out of the box by this component. + * + * @param <Q> + * - The set of request parameters as expected by the method being invoked + * @param <S> + * - The response to be returned by the method being invoked + */ +public interface LdapOperationsFunction<Q, S> { + + /** + * @param ldapOperations + * - An instance of {@link LdapOperations} + * @param request + * - Any object needed by the {@link LdapOperations} method being invoked + * @return - result of the {@link LdapOperations} method being invoked + */ + S apply(LdapOperations ldapOperations, Q request); + +} http://git-wip-us.apache.org/repos/asf/camel/blob/1195acaa/components/camel-spring-ldap/src/main/java/org/apache/camel/component/springldap/SpringLdapProducer.java ---------------------------------------------------------------------- diff --git a/components/camel-spring-ldap/src/main/java/org/apache/camel/component/springldap/SpringLdapProducer.java b/components/camel-spring-ldap/src/main/java/org/apache/camel/component/springldap/SpringLdapProducer.java index 2649427..3a06d75 100644 --- a/components/camel-spring-ldap/src/main/java/org/apache/camel/component/springldap/SpringLdapProducer.java +++ b/components/camel-spring-ldap/src/main/java/org/apache/camel/component/springldap/SpringLdapProducer.java @@ -16,25 +16,36 @@ */ package org.apache.camel.component.springldap; +import static org.apache.camel.component.springldap.LdapOperation.FUNCTION_DRIVEN; + import java.util.Map; import javax.naming.NamingException; import javax.naming.directory.Attributes; +import javax.naming.directory.ModificationItem; import org.apache.camel.Exchange; import org.apache.camel.impl.DefaultProducer; +import org.apache.commons.lang.StringUtils; import org.springframework.ldap.core.AttributesMapper; +import org.springframework.ldap.core.LdapOperations; import org.springframework.ldap.core.LdapTemplate; +import org.springframework.ldap.query.LdapQueryBuilder; public class SpringLdapProducer extends DefaultProducer { public static final String DN = "dn"; public static final String FILTER = "filter"; public static final String ATTRIBUTES = "attributes"; + public static final String PASSWORD = "password"; + public static final String MODIFICATION_ITEMS = "modificationItems"; + + public static final String FUNCTION = "function"; + public static final String REQUEST = "request"; SpringLdapEndpoint endpoint; - private AttributesMapper mapper = new AttributesMapper() { + private AttributesMapper<Object> mapper = new AttributesMapper<Object>() { @Override public Object mapFromAttributes(Attributes attributes) throws NamingException { @@ -51,52 +62,67 @@ public class SpringLdapProducer extends DefaultProducer { } /** - * Performs the LDAP operation defined in SpringLdapEndpoint that created this producer. - * The in-message in the exchange must be a map, containing the following entries: + * Performs the LDAP operation defined in SpringLdapEndpoint that created + * this producer. The in-message in the exchange must be a map, containing + * the following entries: + * * <pre> * key: "dn" - base DN for the LDAP operation * key: "filter" - necessary for the search operation only; LDAP filter for the search operation, * see <a http://en.wikipedia.org/wiki/Lightweight_Directory_Access_Protocol>http://en.wikipedia.org/wiki/Lightweight_Directory_Access_Protocol</a> * key: "attributes" - necessary for the bind operation only; an instance of javax.naming.directory.Attributes, * containing the information necessary to create an LDAP node. + * key: "password" - necessary for the authentication operation only; + * key: "modificationItems" - necessary for the modify_attributes operation only; + * key: "function" - necessary for the function_driven operation only; provides a flexible hook into the {@link LdapTemplate} to call any method + * key: "request" - necessary for the function_driven operation only; passed into the "function" to enable the client to bind parameters that need to be passed into the {@link LdapTemplate} * </pre> + * * The keys are defined as final fields above. */ @Override public void process(Exchange exchange) throws Exception { @SuppressWarnings("unchecked") Map<String, Object> body = exchange.getIn().getBody(Map.class); - String dn = (String) body.get(DN); - - if (null == dn || dn.length() == 0) { - throw new UnsupportedOperationException( - "DN must not be empty, but you provided an empty DN"); - } LdapOperation operation = endpoint.getOperation(); - LdapTemplate ldapTemplate = endpoint.getLdapTemplate(); - if (null == operation) { - throw new UnsupportedOperationException( - "LDAP operation must not be empty, but you provided an empty operation"); + throw new UnsupportedOperationException("LDAP operation must not be empty, but you provided an empty operation"); + } + + String dn = (String)body.get(DN); + if (operation != FUNCTION_DRIVEN && (StringUtils.isBlank(dn))) { + throw new UnsupportedOperationException("DN must not be empty, but you provided an empty DN"); } + LdapOperations ldapTemplate = endpoint.getLdapTemplate(); switch (operation) { case SEARCH: - String filter = (String) body.get(FILTER); + String filter = (String)body.get(FILTER); exchange.getIn().setBody(ldapTemplate.search(dn, filter, endpoint.scopeValue(), mapper)); break; case BIND: - Attributes attributes = (Attributes) body.get(ATTRIBUTES); + Attributes attributes = (Attributes)body.get(ATTRIBUTES); ldapTemplate.bind(dn, null, attributes); break; case UNBIND: ldapTemplate.unbind(dn); break; + case AUTHENTICATE: + ldapTemplate.authenticate(LdapQueryBuilder.query().base(dn).filter((String)body.get(FILTER)), (String)body.get(PASSWORD)); + break; + case MODIFY_ATTRIBUTES: + ModificationItem[] modificationItems = (ModificationItem[])body.get(MODIFICATION_ITEMS); + ldapTemplate.modifyAttributes(dn, modificationItems); + break; + case FUNCTION_DRIVEN: + LdapOperationsFunction<Object, ?> ldapOperationFunction = (LdapOperationsFunction<Object, ?>)body.get(FUNCTION); + Object ldapOperationRequest = body.get(REQUEST); + exchange.getIn().setBody(ldapOperationFunction.apply(ldapTemplate, ldapOperationRequest)); + break; default: - throw new UnsupportedOperationException( - "Bug in the Spring-LDAP component. Despite of all assertions, you managed to call an unsupported operation '" - + operation + "'"); + throw new UnsupportedOperationException( + "Bug in the Spring-LDAP component. Despite of all assertions, you managed to call an unsupported operation '" + operation + "'"); } } } http://git-wip-us.apache.org/repos/asf/camel/blob/1195acaa/components/camel-spring-ldap/src/test/java/org/apache/camel/component/springldap/SpringLdapProducerTest.java ---------------------------------------------------------------------- diff --git a/components/camel-spring-ldap/src/test/java/org/apache/camel/component/springldap/SpringLdapProducerTest.java b/components/camel-spring-ldap/src/test/java/org/apache/camel/component/springldap/SpringLdapProducerTest.java index ea014ed..8fb304c 100644 --- a/components/camel-spring-ldap/src/test/java/org/apache/camel/component/springldap/SpringLdapProducerTest.java +++ b/components/camel-spring-ldap/src/test/java/org/apache/camel/component/springldap/SpringLdapProducerTest.java @@ -16,38 +16,38 @@ */ package org.apache.camel.component.springldap; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.eq; +import static org.mockito.Matchers.isNull; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + import java.util.HashMap; import java.util.Map; +import javax.naming.directory.BasicAttribute; import javax.naming.directory.BasicAttributes; +import javax.naming.directory.DirContext; +import javax.naming.directory.ModificationItem; import javax.naming.directory.SearchControls; import org.apache.camel.Exchange; import org.apache.camel.Message; import org.apache.camel.impl.DefaultExchange; import org.apache.camel.impl.DefaultMessage; - import org.apache.camel.test.junit4.CamelTestSupport; - import org.junit.Before; import org.junit.Test; - +import org.mockito.Matchers; import org.mockito.Mockito; - import org.springframework.ldap.core.AttributesMapper; +import org.springframework.ldap.core.LdapOperations; import org.springframework.ldap.core.LdapTemplate; - -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.eq; -import static org.mockito.Matchers.isNull; - -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; +import org.springframework.ldap.query.LdapQuery; public class SpringLdapProducerTest extends CamelTestSupport { - private SpringLdapEndpoint ldapEndpoint = Mockito - .mock(SpringLdapEndpoint.class); + private SpringLdapEndpoint ldapEndpoint = Mockito.mock(SpringLdapEndpoint.class); private LdapTemplate ldapTemplate = Mockito.mock(LdapTemplate.class); private SpringLdapProducer ldapProducer = new SpringLdapProducer(ldapEndpoint); @@ -82,9 +82,21 @@ public class SpringLdapProducerTest extends CamelTestSupport { processBody(exchange, in, body); } + + @Test + public void testNoDN_FunctionDrivenOperation() throws Exception { + Exchange exchange = new DefaultExchange(context); + Message in = new DefaultMessage(); + + Map<String, Object> body = new HashMap<String, Object>(); + body.put(SpringLdapProducer.FUNCTION, Mockito.mock(LdapOperationsFunction.class)); + + when(ldapEndpoint.getOperation()).thenReturn(LdapOperation.FUNCTION_DRIVEN); + + processBody(exchange, in, body); + } - private void processBody(Exchange exchange, Message message, - Map<String, Object> body) throws Exception { + private void processBody(Exchange exchange, Message message, Map<String, Object> body) throws Exception { message.setBody(body); exchange.setIn(message); ldapProducer.process(exchange); @@ -140,8 +152,7 @@ public class SpringLdapProducerTest extends CamelTestSupport { when(ldapEndpoint.scopeValue()).thenReturn(scope); processBody(exchange, in, body); - verify(ldapTemplate).search(eq(dn), eq(filter), eq(scope), - any(AttributesMapper.class)); + verify(ldapTemplate).search(eq(dn), eq(filter), eq(scope), any(AttributesMapper.class)); } @Test @@ -178,4 +189,67 @@ public class SpringLdapProducerTest extends CamelTestSupport { verify(ldapTemplate).unbind(eq(dn)); } + @Test + public void testAuthenticate() throws Exception { + String dn = "cn=dn"; + String filter = "filter"; + String password = "password"; + + Exchange exchange = new DefaultExchange(context); + Message in = new DefaultMessage(); + + Map<String, Object> body = new HashMap<String, Object>(); + body.put(SpringLdapProducer.DN, dn); + body.put(SpringLdapProducer.FILTER, filter); + body.put(SpringLdapProducer.PASSWORD, password); + + when(ldapEndpoint.getOperation()).thenReturn(LdapOperation.AUTHENTICATE); + + processBody(exchange, in, body); + verify(ldapTemplate).authenticate(Matchers.any(LdapQuery.class), eq(password)); + } + + @Test + public void testModifyAttributes() throws Exception { + String dn = "cn=dn"; + ModificationItem[] modificationItems = new ModificationItem[] {new ModificationItem(DirContext.ADD_ATTRIBUTE, new BasicAttribute("key", "value"))}; + + Exchange exchange = new DefaultExchange(context); + Message in = new DefaultMessage(); + + Map<String, Object> body = new HashMap<String, Object>(); + body.put(SpringLdapProducer.DN, dn); + body.put(SpringLdapProducer.MODIFICATION_ITEMS, modificationItems); + + when(ldapEndpoint.getOperation()).thenReturn(LdapOperation.MODIFY_ATTRIBUTES); + + processBody(exchange, in, body); + verify(ldapTemplate).modifyAttributes(eq(dn), eq(modificationItems)); + } + + @Test + public void testFunctionDriven() throws Exception { + String dn = "cn=dn"; + LdapOperationsFunction<String, Void> function = new LdapOperationsFunction<String, Void>() { + @Override + public Void apply(LdapOperations ldapOperations, String request) { + ldapOperations.lookup(request); + return null; + } + }; + + Exchange exchange = new DefaultExchange(context); + Message in = new DefaultMessage(); + + Map<String, Object> body = new HashMap<String, Object>(); + body.put(SpringLdapProducer.DN, dn); + body.put(SpringLdapProducer.REQUEST, dn); + body.put(SpringLdapProducer.FUNCTION, function); + + when(ldapEndpoint.getOperation()).thenReturn(LdapOperation.FUNCTION_DRIVEN); + + processBody(exchange, in, body); + verify(ldapTemplate).lookup(eq(dn)); + } + }
