This is an automated email from the ASF dual-hosted git repository. jamesnetherton pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/camel-quarkus.git
The following commit(s) were added to refs/heads/main by this push: new ae87823057 fix of #7226: ldap config optimization + tests refactor ae87823057 is described below commit ae87823057738b523a3d58d9537e22d45f1b6e61 Author: Jiri Ondrusek <ondrusek.j...@gmail.com> AuthorDate: Fri Apr 4 13:44:08 2025 +0200 fix of #7226: ldap config optimization + tests refactor --- .../ROOT/pages/reference/extensions/ldap.adoc | 96 ++++++++++++++++++ .../component/ldap/deployment/LdapProcessor.java | 16 +++ extensions/ldap/runtime/src/main/doc/usage.adoc | 36 +++++++ .../quarkus/component/ldap/CamelLdapConfig.java | 81 ++++++++++++++++ .../quarkus/component/ldap/CamelLdapRecorder.java | 50 ++++++++++ .../component/ldap/it/CustomSSLSocketFactory.java | 12 +-- .../quarkus/component/ldap/it/LdapProducer.java | 51 ++++++++++ .../quarkus/component/ldap/it/LdapResource.java | 70 ++------------ .../quarkus/component/ldap/it/LdapRoutes.java | 14 ++- .../ldap/src/main/resources/application.properties | 30 ++++++ .../camel/quarkus/component/ldap/it/LdapTest.java | 107 ++------------------- .../component/ldap/it/LdapTestResource.java | 101 +++++++++++++++++++ 12 files changed, 491 insertions(+), 173 deletions(-) diff --git a/docs/modules/ROOT/pages/reference/extensions/ldap.adoc b/docs/modules/ROOT/pages/reference/extensions/ldap.adoc index 8194a85386..c9eaef3000 100644 --- a/docs/modules/ROOT/pages/reference/extensions/ldap.adoc +++ b/docs/modules/ROOT/pages/reference/extensions/ldap.adoc @@ -46,6 +46,43 @@ endif::[] [id="extensions-ldap-usage"] == Usage +[id="extensions-ldap-usage-configuration-via-properties"] +=== Configuration via properties + +The LDAP component supports property-based configuration in addition to plain Camel configuration. +Configuration property should follow the pattern: + +[source,properties] +---- +quarkus.camel.ldap.dir-contexts.<dirContextName>.<property> +---- + +For more details, refer to the dirContext https://docs.oracle.com/en/java/javase/17/docs/api/java.naming/javax/naming/directory/DirContext.html[Javadoc]. +The following options are available: + +* initial-context-factory +* provider-url +* security-authentication +* security-protocol +* socket-factory + +If you need to specify an option that is not listed above, use the following property format: + +[source,properties] +---- +quarkus.camel.ldap.dr-contexts."your_name".additional-options."not-listed_option_name" +---- + +Here is an example of configuration of a dirContext named `your_context` using SSL. + +[source,properties] +---- +quarkus.camel.ldap.dir-contexts."your_context".initial-context-factory=com.sun.jndi.ldap.LdapCtxFactory +quarkus.camel.ldap.dir-contexts."your_context".provider-url=ldaps://${ldap.host}:${ldap.sslPort} +quarkus.camel.ldap.dir-contexts."your_context".security-protocol=ssl +quarkus.camel.ldap.dir-contexts."your_context".socket-factory=org.apache.camel.quarkus.component.ldap.it.CustomSSLSocketFactory +---- + [id="extensions-ldap-usage-using-ssl-in-native-mode"] === Using SSL in Native Mode @@ -59,3 +96,62 @@ public class CustomSSLSocketFactory extends SSLSocketFactory { } ---- + +[id="extensions-ldap-additional-camel-quarkus-configuration"] +== Additional Camel Quarkus configuration + +[width="100%",cols="80,5,15",options="header"] +|=== +| Configuration property | Type | Default + + +a| [[quarkus-camel-ldap-dir-contexts-dir-contexts-initial-context-factory]]`link:#quarkus-camel-ldap-dir-contexts-dir-contexts-initial-context-factory[quarkus.camel.ldap.dir-contexts."dir-contexts".initial-context-factory]` + +The initial context factory to use. The value of the property should be the fully qualified class name +of the factory class that will create an initial context. +| `string` +| + +a| [[quarkus-camel-ldap-dir-contexts-dir-contexts-provider-url]]`link:#quarkus-camel-ldap-dir-contexts-dir-contexts-provider-url[quarkus.camel.ldap.dir-contexts."dir-contexts".provider-url]` + +The service provider +to use. The value of the property should contain a URL string +(e.g. "ldap://somehost:389"). +| `string` +| + +a| [[quarkus-camel-ldap-dir-contexts-dir-contexts-security-protocol]]`link:#quarkus-camel-ldap-dir-contexts-dir-contexts-security-protocol[quarkus.camel.ldap.dir-contexts."dir-contexts".security-protocol]` + +The security protocol to use. +Its value is a string determined by the service provider +(e.g. "ssl"). +| `string` +| + +a| [[quarkus-camel-ldap-dir-contexts-dir-contexts-security-authentication]]`link:#quarkus-camel-ldap-dir-contexts-dir-contexts-security-authentication[quarkus.camel.ldap.dir-contexts."dir-contexts".security-authentication]` + +The security level to use. +Its value is one of the following strings: +"none", "simple", "strong". +If this property is unspecified, +the behaviour is determined by the service provider. +| `string` +| `none` + +a| [[quarkus-camel-ldap-dir-contexts-dir-contexts-socket-factory]]`link:#quarkus-camel-ldap-dir-contexts-dir-contexts-socket-factory[quarkus.camel.ldap.dir-contexts."dir-contexts".socket-factory]` + +The custom socket factory to use. The value of the property should be the fully qualified class name +of the socket factory class. +| `string` +| + +a| [[quarkus-camel-ldap-dir-contexts-dir-contexts-additional-options-additional-options]]`link:#quarkus-camel-ldap-dir-contexts-dir-contexts-additional-options-additional-options[quarkus.camel.ldap.dir-contexts."dir-contexts".additional-options."additional-options"]` + +Any other option which will be used during dirContext creation. +| `Map<String,String>` +| +|=== + +[.configuration-legend] +{doc-link-icon-lock}[title=Fixed at build time] Configuration property fixed at build time. All other configuration properties are overridable at runtime. + diff --git a/extensions/ldap/deployment/src/main/java/org/apache/camel/quarkus/component/ldap/deployment/LdapProcessor.java b/extensions/ldap/deployment/src/main/java/org/apache/camel/quarkus/component/ldap/deployment/LdapProcessor.java index c5dd3ede23..8daa0d2249 100644 --- a/extensions/ldap/deployment/src/main/java/org/apache/camel/quarkus/component/ldap/deployment/LdapProcessor.java +++ b/extensions/ldap/deployment/src/main/java/org/apache/camel/quarkus/component/ldap/deployment/LdapProcessor.java @@ -18,9 +18,14 @@ package org.apache.camel.quarkus.component.ldap.deployment; import io.quarkus.deployment.annotations.BuildProducer; import io.quarkus.deployment.annotations.BuildStep; +import io.quarkus.deployment.annotations.ExecutionTime; +import io.quarkus.deployment.annotations.Record; import io.quarkus.deployment.builditem.AllowJNDIBuildItem; import io.quarkus.deployment.builditem.FeatureBuildItem; import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem; +import org.apache.camel.quarkus.component.ldap.CamelLdapConfig; +import org.apache.camel.quarkus.component.ldap.CamelLdapRecorder; +import org.apache.camel.quarkus.core.deployment.spi.CamelContextBuildItem; class LdapProcessor { @@ -43,4 +48,15 @@ class LdapProcessor { "com.sun.jndi.rmi.registry.RegistryContextFactory") .build()); } + + @BuildStep + @Record(ExecutionTime.RUNTIME_INIT) + void createDirContexts( + CamelContextBuildItem context, + CamelLdapRecorder camelLdapRecorder, + CamelLdapConfig ldapConfig) { + + camelLdapRecorder.createDirContexts(context.getCamelContext(), ldapConfig); + } + } diff --git a/extensions/ldap/runtime/src/main/doc/usage.adoc b/extensions/ldap/runtime/src/main/doc/usage.adoc index dbb52638a8..4a94ae4929 100644 --- a/extensions/ldap/runtime/src/main/doc/usage.adoc +++ b/extensions/ldap/runtime/src/main/doc/usage.adoc @@ -1,3 +1,39 @@ +=== Configuration via properties + +The LDAP component supports property-based configuration in addition to plain Camel configuration. +Configuration property should follow the pattern: + +[source,properties] +---- +quarkus.camel.ldap.dir-contexts.<dirContextName>.<property> +---- + +For more details, refer to the dirContext https://docs.oracle.com/en/java/javase/17/docs/api/java.naming/javax/naming/directory/DirContext.html[Javadoc]. +The following options are available: + +* initial-context-factory +* provider-url +* security-authentication +* security-protocol +* socket-factory + +If you need to specify an option that is not listed above, use the following property format: + +[source,properties] +---- +quarkus.camel.ldap.dr-contexts."your_name".additional-options."not-listed_option_name" +---- + +Here is an example of configuration of a dirContext named `your_context` using SSL. + +[source,properties] +---- +quarkus.camel.ldap.dir-contexts."your_context".initial-context-factory=com.sun.jndi.ldap.LdapCtxFactory +quarkus.camel.ldap.dir-contexts."your_context".provider-url=ldaps://${ldap.host}:${ldap.sslPort} +quarkus.camel.ldap.dir-contexts."your_context".security-protocol=ssl +quarkus.camel.ldap.dir-contexts."your_context".socket-factory=org.apache.camel.quarkus.component.ldap.it.CustomSSLSocketFactory +---- + === Using SSL in Native Mode When using a custom `SSLSocketFactory` in native mode, such as the one in the xref:{cq-camel-components}::ldap-component.adoc#_configuring_ssl[Configuring SSL] section, you need to register the class for reflection otherwise the class will not be made available on the classpath. Add the `@RegisterForReflection` annotation above the class definition, as follows: diff --git a/extensions/ldap/runtime/src/main/java/org/apache/camel/quarkus/component/ldap/CamelLdapConfig.java b/extensions/ldap/runtime/src/main/java/org/apache/camel/quarkus/component/ldap/CamelLdapConfig.java new file mode 100644 index 0000000000..9036fbac4e --- /dev/null +++ b/extensions/ldap/runtime/src/main/java/org/apache/camel/quarkus/component/ldap/CamelLdapConfig.java @@ -0,0 +1,81 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.camel.quarkus.component.ldap; + +import java.util.Map; +import java.util.Optional; + +import io.quarkus.runtime.annotations.ConfigGroup; +import io.quarkus.runtime.annotations.ConfigPhase; +import io.quarkus.runtime.annotations.ConfigRoot; +import io.smallrye.config.ConfigMapping; +import io.smallrye.config.WithDefault; + +@ConfigRoot(phase = ConfigPhase.RUN_TIME) +@ConfigMapping(prefix = "quarkus.camel.ldap") +public interface CamelLdapConfig { + + /** + * Ldap dirContext configuration + */ + Map<String, LdapDirContextConfig> dirContexts(); + + @ConfigGroup + interface LdapDirContextConfig { + + /** + * The initial context factory to use. The value of the property should be the fully qualified class name + * of the factory class that will create an initial context. + */ + Optional<String> initialContextFactory(); + + /** + * The service provider + * to use. The value of the property should contain a URL string + * (e.g. "ldap://somehost:389"). + */ + Optional<String> providerUrl(); + + /** + * The security protocol to use. + * Its value is a string determined by the service provider + * (e.g. "ssl"). + */ + Optional<String> securityProtocol(); + + /** + * The security level to use. + * Its value is one of the following strings: + * "none", "simple", "strong". + * If this property is unspecified, + * the behaviour is determined by the service provider. + */ + @WithDefault("none") + String securityAuthentication(); + + /** + * The custom socket factory to use. The value of the property should be the fully qualified class name + * of the socket factory class. + */ + Optional<String> socketFactory(); + + /** + * Any other option which will be used during dirContext creation. + */ + Map<String, String> additionalOptions(); + } +} diff --git a/extensions/ldap/runtime/src/main/java/org/apache/camel/quarkus/component/ldap/CamelLdapRecorder.java b/extensions/ldap/runtime/src/main/java/org/apache/camel/quarkus/component/ldap/CamelLdapRecorder.java new file mode 100644 index 0000000000..9ee3f8dab8 --- /dev/null +++ b/extensions/ldap/runtime/src/main/java/org/apache/camel/quarkus/component/ldap/CamelLdapRecorder.java @@ -0,0 +1,50 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.camel.quarkus.component.ldap; + +import java.util.Hashtable; + +import javax.naming.Context; + +import io.quarkus.runtime.RuntimeValue; +import io.quarkus.runtime.annotations.Recorder; +import org.apache.camel.CamelContext; + +@Recorder +public class CamelLdapRecorder { + + public void createDirContexts(RuntimeValue<CamelContext> contextRuntimeValue, final CamelLdapConfig config) { + CamelContext context = contextRuntimeValue.getValue(); + + config.dirContexts().keySet().forEach(contextName -> { + + CamelLdapConfig.LdapDirContextConfig dirConfig = config.dirContexts().get(contextName); + + Hashtable<String, Object> env = new Hashtable<String, Object>(); + dirConfig.initialContextFactory().ifPresent(v -> env.put(Context.INITIAL_CONTEXT_FACTORY, v)); + dirConfig.providerUrl().ifPresent(v -> env.put(Context.PROVIDER_URL, v)); + env.put(Context.SECURITY_AUTHENTICATION, dirConfig.securityAuthentication()); + dirConfig.securityProtocol().ifPresent(v -> env.put(Context.SECURITY_PROTOCOL, v)); + dirConfig.socketFactory().ifPresent(v -> env.put("java.naming.ldap.factory.socket", v)); + + //additional options + dirConfig.additionalOptions().entrySet().forEach(e -> env.put(e.getKey(), e.getValue())); + + context.getRegistry().bind(contextName, env); + }); + } +} diff --git a/integration-tests/ldap/src/main/java/org/apache/camel/quarkus/component/ldap/it/CustomSSLSocketFactory.java b/integration-tests/ldap/src/main/java/org/apache/camel/quarkus/component/ldap/it/CustomSSLSocketFactory.java index 932401bc93..6719c1da6e 100644 --- a/integration-tests/ldap/src/main/java/org/apache/camel/quarkus/component/ldap/it/CustomSSLSocketFactory.java +++ b/integration-tests/ldap/src/main/java/org/apache/camel/quarkus/component/ldap/it/CustomSSLSocketFactory.java @@ -36,13 +36,10 @@ public class CustomSSLSocketFactory extends SSLSocketFactory { private final SSLSocketFactory delegate; - private static String trustStoreFilename; - private static String trustStorePassword; - public CustomSSLSocketFactory() throws Exception { KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType()); - try (InputStream in = new FileInputStream(trustStoreFilename)) { - trustStore.load(in, trustStorePassword.toCharArray()); + try (InputStream in = new FileInputStream("target/certs/ldap-truststore.p12")) { + trustStore.load(in, "changeit".toCharArray()); } TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); @@ -99,9 +96,4 @@ public class CustomSSLSocketFactory extends SSLSocketFactory { throws IOException { return delegate.createSocket(address, port, localAddress, localPort); } - - public static void setTrustStore(String fileName, String password) { - trustStoreFilename = fileName; - trustStorePassword = password; - } } diff --git a/integration-tests/ldap/src/main/java/org/apache/camel/quarkus/component/ldap/it/LdapProducer.java b/integration-tests/ldap/src/main/java/org/apache/camel/quarkus/component/ldap/it/LdapProducer.java new file mode 100644 index 0000000000..091583a817 --- /dev/null +++ b/integration-tests/ldap/src/main/java/org/apache/camel/quarkus/component/ldap/it/LdapProducer.java @@ -0,0 +1,51 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.camel.quarkus.component.ldap.it; + +import java.util.Hashtable; + +import javax.naming.Context; +import javax.naming.directory.DirContext; +import javax.naming.directory.InitialDirContext; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.context.Dependent; +import jakarta.enterprise.inject.Produces; +import jakarta.inject.Named; +import org.eclipse.microprofile.config.inject.ConfigProperty; + +@ApplicationScoped +public class LdapProducer { + + @ConfigProperty(name = "ldap.port") + Integer ldapPort; + + @ConfigProperty(name = "ldap.host") + String ldapHost; + + @Produces + @Dependent + @Named("originalConfig") + public DirContext createLdapContext() throws Exception { + + Hashtable<String, Object> env = new Hashtable<>(); + env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory"); + env.put(Context.PROVIDER_URL, String.format("ldap://%s:%s", ldapHost, ldapPort)); + env.put(Context.SECURITY_AUTHENTICATION, "none"); + return new InitialDirContext(env); + } +} diff --git a/integration-tests/ldap/src/main/java/org/apache/camel/quarkus/component/ldap/it/LdapResource.java b/integration-tests/ldap/src/main/java/org/apache/camel/quarkus/component/ldap/it/LdapResource.java index 5b01ecbaef..03ae765b57 100644 --- a/integration-tests/ldap/src/main/java/org/apache/camel/quarkus/component/ldap/it/LdapResource.java +++ b/integration-tests/ldap/src/main/java/org/apache/camel/quarkus/component/ldap/it/LdapResource.java @@ -18,25 +18,18 @@ package org.apache.camel.quarkus.component.ldap.it; import java.util.ArrayList; import java.util.HashMap; -import java.util.Hashtable; import java.util.List; import java.util.Map; -import javax.naming.Context; import javax.naming.NamingEnumeration; import javax.naming.directory.Attribute; -import javax.naming.directory.DirContext; -import javax.naming.directory.InitialDirContext; import javax.naming.directory.SearchResult; import jakarta.enterprise.context.ApplicationScoped; -import jakarta.enterprise.context.Dependent; import jakarta.inject.Inject; -import jakarta.inject.Named; -import jakarta.ws.rs.Consumes; import jakarta.ws.rs.GET; -import jakarta.ws.rs.POST; import jakarta.ws.rs.Path; +import jakarta.ws.rs.PathParam; import jakarta.ws.rs.Produces; import jakarta.ws.rs.QueryParam; import jakarta.ws.rs.core.MediaType; @@ -52,72 +45,29 @@ public class LdapResource { @Inject CamelContext camelContext; - private String ldapHost; - private String ldapPort; - private boolean useSSL; - private String trustStoreFilename; - private String trustStorePassword; - - /** - * Extracts the LDAP connection parameters passed from the test and creates a - * {@link javax.naming.directory.DirContext} from them. - * The DirContext is then bound into the CamelContext for use in the LDAP route. - * - * @param options - * @throws Exception - */ - @Path("/configure") - @POST - @Consumes(MediaType.APPLICATION_JSON) - public void configure(Map<String, String> options) throws Exception { - ldapHost = options.get("host"); - ldapPort = options.get("port"); - useSSL = Boolean.valueOf(options.get("ssl")); - trustStoreFilename = options.get("trustStore"); - trustStorePassword = options.get("trustStorePassword"); - } - - @Path("/search") + @Path("/search/{direct}") @GET @Produces(MediaType.APPLICATION_JSON) - public Response search(@QueryParam("ldapQuery") String ldapQuery) throws Exception { - return Response.ok(searchByUid(ldapQuery)).build(); + public Response search(@PathParam("direct") String directName, + @QueryParam("ldapQuery") String ldapQuery) throws Exception { + return Response.ok(searchByUid(directName, ldapQuery)).build(); } @Path("/safeSearch") @GET @Produces(MediaType.APPLICATION_JSON) public Response safeSearch(@QueryParam("ldapQuery") String ldapQuery) throws Exception { - return Response.ok(searchByUid(LdapHelper.escapeFilter(ldapQuery))).build(); + return Response.ok(searchByUid("http", LdapHelper.escapeFilter(ldapQuery))).build(); } @SuppressWarnings("unchecked") - private List<Map<String, String>> searchByUid(String uid) throws Exception { + private List<Map<String, String>> searchByUid(String directName, String uid) throws Exception { String filter = String.format("(uid=%s)", uid); ProducerTemplate producer = camelContext.createProducerTemplate(); - List<SearchResult> results = producer.requestBody("direct:start", filter, List.class); + List<SearchResult> results = producer.requestBody("direct:" + directName, filter, List.class); return convertSearchResults(results); } - @Produces - @Dependent - @Named("ldapserver") - public DirContext createLdapContext() throws Exception { - String scheme = useSSL ? "ldaps" : "ldap"; - Hashtable<String, Object> env = new Hashtable<>(); - env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory"); - env.put(Context.PROVIDER_URL, String.format("%s://%s:%s", scheme, ldapHost, ldapPort)); - env.put(Context.SECURITY_AUTHENTICATION, "none"); - - if (useSSL) { - CustomSSLSocketFactory.setTrustStore(trustStoreFilename, trustStorePassword); - env.put("java.naming.ldap.factory.socket", CustomSSLSocketFactory.class.getName()); - env.put(Context.SECURITY_PROTOCOL, "ssl"); - } - - return new InitialDirContext(env); - } - /** * Converts the list of {@link javax.naming.directory.SearchResult} objects into * a structure that Jackson can @@ -128,10 +78,10 @@ public class LdapResource { * @throws Exception */ private List<Map<String, String>> convertSearchResults(List<SearchResult> searchResults) throws Exception { - List<Map<String, String>> results = new ArrayList<>(); + List<Map<String, String>> results = new ArrayList<Map<String, String>>(); for (SearchResult searchResult : searchResults) { - Map<String, String> resultMap = new HashMap<>(); + Map<String, String> resultMap = new HashMap<String, String>(); NamingEnumeration<? extends Attribute> attrs = searchResult.getAttributes().getAll(); while (attrs.hasMore()) { Attribute attr = attrs.next(); diff --git a/integration-tests/ldap/src/main/java/org/apache/camel/quarkus/component/ldap/it/LdapRoutes.java b/integration-tests/ldap/src/main/java/org/apache/camel/quarkus/component/ldap/it/LdapRoutes.java index bfd07fa29c..c96d91466a 100644 --- a/integration-tests/ldap/src/main/java/org/apache/camel/quarkus/component/ldap/it/LdapRoutes.java +++ b/integration-tests/ldap/src/main/java/org/apache/camel/quarkus/component/ldap/it/LdapRoutes.java @@ -22,8 +22,18 @@ public class LdapRoutes extends RouteBuilder { @Override public void configure() throws Exception { - from("direct:start") - .to("ldap://ldapserver?base=ou=system"); + + from("direct:http") + .to("ldap://httpserver?base=ou=system"); + + from("direct:ssl") + .to("ldap://sslserver?base=ou=system"); + + from("direct:additionalOptions") + .to("ldap://additional?base=ou=system"); + + from("direct:originalConfig") + .to("ldap://additional?base=ou=system"); } } diff --git a/integration-tests/ldap/src/main/resources/application.properties b/integration-tests/ldap/src/main/resources/application.properties new file mode 100644 index 0000000000..64f2e94977 --- /dev/null +++ b/integration-tests/ldap/src/main/resources/application.properties @@ -0,0 +1,30 @@ +## --------------------------------------------------------------------------- +## Licensed to the Apache Software Foundation (ASF) under one or more +## contributor license agreements. See the NOTICE file distributed with +## this work for additional information regarding copyright ownership. +## The ASF licenses this file to You 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. +## --------------------------------------------------------------------------- +quarkus.camel.ldap.dir-contexts."httpserver".initial-context-factory=com.sun.jndi.ldap.LdapCtxFactory +quarkus.camel.ldap.dir-contexts."httpserver".provider-url=ldap://${ldap.host}:${ldap.port} +quarkus.camel.ldap.dir-contexts."httpserver".security-authentication=none + +quarkus.camel.ldap.dir-contexts."sslserver".initial-context-factory=com.sun.jndi.ldap.LdapCtxFactory +quarkus.camel.ldap.dir-contexts."sslserver".provider-url=ldaps://${ldap.host}:${ldap.sslPort} +quarkus.camel.ldap.dir-contexts."sslserver".security-protocol=ssl +quarkus.camel.ldap.dir-contexts."sslserver".socket-factory=org.apache.camel.quarkus.component.ldap.it.CustomSSLSocketFactory + +#test of addition-options +quarkus.camel.ldap.dir-contexts."additional".initial-context-factory=com.sun.jndi.ldap.LdapCtxFactory +quarkus.camel.ldap.dir-contexts."additional".provider-url=ldaps://${ldap.host}:${ldap.sslPort} +quarkus.camel.ldap.dir-contexts."additional".additional-options.security-protocol=ssl +quarkus.camel.ldap.dir-contexts."additional".socket-factory=org.apache.camel.quarkus.component.ldap.it.CustomSSLSocketFactory diff --git a/integration-tests/ldap/src/test/java/org/apache/camel/quarkus/component/ldap/it/LdapTest.java b/integration-tests/ldap/src/test/java/org/apache/camel/quarkus/component/ldap/it/LdapTest.java index 706273d174..20ec562f0e 100644 --- a/integration-tests/ldap/src/test/java/org/apache/camel/quarkus/component/ldap/it/LdapTest.java +++ b/integration-tests/ldap/src/test/java/org/apache/camel/quarkus/component/ldap/it/LdapTest.java @@ -16,31 +16,17 @@ */ package org.apache.camel.quarkus.component.ldap.it; -import java.io.InputStream; -import java.net.InetAddress; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.stream.Collectors; -import com.unboundid.ldap.listener.InMemoryDirectoryServer; -import com.unboundid.ldap.listener.InMemoryDirectoryServerConfig; -import com.unboundid.ldap.listener.InMemoryListenerConfig; -import com.unboundid.ldif.LDIFReader; -import com.unboundid.util.ssl.KeyStoreKeyManager; -import com.unboundid.util.ssl.SSLUtil; +import io.quarkus.test.common.QuarkusTestResource; import io.quarkus.test.junit.QuarkusTest; import io.restassured.RestAssured; import io.restassured.common.mapper.TypeRef; import io.smallrye.certs.Format; import io.smallrye.certs.junit5.Certificate; -import jakarta.ws.rs.core.MediaType; import org.apache.camel.quarkus.test.support.certificate.TestCertificates; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; @@ -51,74 +37,23 @@ import static org.junit.jupiter.api.Assertions.assertEquals; @Certificate(name = "ldap", formats = { Format.PKCS12 }, password = "changeit") }) @QuarkusTest +@QuarkusTestResource(LdapTestResource.class) class LdapTest { - private static InMemoryDirectoryServer ldapServer = null; - private static final String truststoreFile = "target/certs/ldap-truststore.p12"; - - @BeforeAll - public static void setUpLdapServer() throws Exception { - - // Create an LDAP server to handle unencrypted and TLS connections - InMemoryDirectoryServerConfig dsConfig = new InMemoryDirectoryServerConfig("ou=system"); - InMemoryListenerConfig listenerConfig = InMemoryListenerConfig.createLDAPConfig("ldap", - InetAddress.getLoopbackAddress(), 0, null); - - // The keystore is generated by the build process - Path keystoreFile = Paths.get("target/certs/ldap-keystore.p12"); - if (!Files.isRegularFile(keystoreFile)) { - /* The test is run from a test-jar within Quarkus Platform, where the Ant script was not run - * so let's copy the keystore from test-jar to the local folder */ - Files.createDirectories(keystoreFile.getParent()); - try (InputStream in = LdapTest.class.getClassLoader().getResourceAsStream(keystoreFile.getFileName().toString())) { - Files.copy(in, keystoreFile); - } - Path truststorePath = Paths.get(truststoreFile); - try (InputStream in = LdapTest.class.getClassLoader() - .getResourceAsStream(truststorePath.getFileName().toString())) { - Files.copy(in, truststorePath); - } - } - - SSLUtil serverSSLUtil = new SSLUtil(new KeyStoreKeyManager(keystoreFile.toFile(), "changeit".toCharArray()), - null); - InMemoryListenerConfig sslListenerConfig = InMemoryListenerConfig.createLDAPSConfig("ldaps", - InetAddress.getLoopbackAddress(), 0, serverSSLUtil.createSSLServerSocketFactory(), - null); - dsConfig.setListenerConfigs(listenerConfig, sslListenerConfig); - ldapServer = new InMemoryDirectoryServer(dsConfig); - - // Load the LDIF file from the Camel LDAP tests - LDIFReader ldifReader = new LDIFReader( - LdapTest.class.getClassLoader().getResourceAsStream("LdapRouteTest.ldif")); - ldapServer.importFromLDIF(true, ldifReader); - ldapServer.startListening(); - } - - @AfterAll - public static void tearDownLdapServer() { - if (ldapServer != null) { - ldapServer.close(); - } - } - /** * Calls a Camel route to search for LDAP entries where the uid is "tcruise". * The test is run in both SSL and non-SSL modes. * - * @param useSSL * @throws Exception */ @ParameterizedTest - @ValueSource(booleans = { false, true }) - public void ldapSearchTest(boolean useSSL) throws Exception { - configureResource(useSSL); - + @ValueSource(strings = { "http", "ssl", "originalConfig", "additionalOptions" }) + public void ldapSearchTest(String direct) throws Exception { TypeRef<List<Map<String, Object>>> typeRef = new TypeRef<>() { }; List<Map<String, Object>> results = RestAssured.given() .queryParam("ldapQuery", "tcruise") - .get("/ldap/search") + .get("/ldap/search/" + direct) .then() .statusCode(200) .extract().as(typeRef); @@ -135,15 +70,13 @@ class LdapTest { */ @Test public void ldapHelperTest() throws Exception { - configureResource(false); - TypeRef<List<Map<String, Object>>> typeRef = new TypeRef<>() { }; // Verfiy that calling the unsafe endpoint with a wildcard returns multiple results. List<Map<String, Object>> results = RestAssured.given() .queryParam("ldapQuery", "test*") - .get("/ldap/search") + .get("/ldap/search/http") .then() .statusCode(200) .extract().as(typeRef); @@ -169,34 +102,6 @@ class LdapTest { .extract().as(typeRef); assertEquals(1, results.size()); assertEquals("test1", results.get(0).get("ou")); - } - /** - * Configures the - * {@link org.apache.camel.quarkus.component.ldap.it.LdapResource} by sending it - * a Map of connection properties. - * - * @param useSSL - * @throws Exception - */ - private void configureResource(boolean useSSL) throws Exception { - // Configure the LdapResource with the connection details for the LDAP server - String listenerName = useSSL ? "ldaps" : "ldap"; - Map<String, String> options = new HashMap<>(); - options.put("host", ldapServer.getListenAddress(listenerName).getHostAddress()); - options.put("port", String.valueOf(ldapServer.getListenPort(listenerName))); - options.put("ssl", String.valueOf(useSSL)); - if (useSSL) { - options.put("trustStore", truststoreFile); - options.put("trustStorePassword", "changeit"); - } - - RestAssured.given() - .body(options) - .contentType(MediaType.APPLICATION_JSON) - .post("/ldap/configure") - .then() - .statusCode(204); - } } diff --git a/integration-tests/ldap/src/test/java/org/apache/camel/quarkus/component/ldap/it/LdapTestResource.java b/integration-tests/ldap/src/test/java/org/apache/camel/quarkus/component/ldap/it/LdapTestResource.java new file mode 100644 index 0000000000..1e7684ae1c --- /dev/null +++ b/integration-tests/ldap/src/test/java/org/apache/camel/quarkus/component/ldap/it/LdapTestResource.java @@ -0,0 +1,101 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.camel.quarkus.component.ldap.it; + +import java.io.InputStream; +import java.net.InetAddress; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Map; + +import com.unboundid.ldap.listener.InMemoryDirectoryServer; +import com.unboundid.ldap.listener.InMemoryDirectoryServerConfig; +import com.unboundid.ldap.listener.InMemoryListenerConfig; +import com.unboundid.ldif.LDIFReader; +import com.unboundid.util.ssl.KeyStoreKeyManager; +import com.unboundid.util.ssl.SSLUtil; +import io.quarkus.test.common.QuarkusTestResourceLifecycleManager; + +public class LdapTestResource implements QuarkusTestResourceLifecycleManager { + + InMemoryDirectoryServer ldapServer; + + @Override + public Map<String, String> start() { + + try { + // Create an LDAP server to handle unencrypted and TLS connections + InMemoryDirectoryServerConfig dsConfig = new InMemoryDirectoryServerConfig("ou=system"); + InMemoryListenerConfig listenerConfig = InMemoryListenerConfig.createLDAPConfig("ldap", + InetAddress.getLoopbackAddress(), 0, null); + + // The keystore is generated by the build process + Path keystoreFile = Paths.get("target/certs/ldap-keystore.p12"); + if (!Files.isRegularFile(keystoreFile)) { + /* The test is run from a test-jar within Quarkus Platform, where the Ant script was not run + * so let's copy the keystore from test-jar to the local folder */ + Files.createDirectories(keystoreFile.getParent()); + try (InputStream in = LdapTest.class.getClassLoader() + .getResourceAsStream(keystoreFile.getFileName().toString())) { + Files.copy(in, keystoreFile); + } + Path truststorePath = Paths.get("target/certs/ldap-truststore.p12"); + try (InputStream in = LdapTest.class.getClassLoader() + .getResourceAsStream(truststorePath.getFileName().toString())) { + Files.copy(in, truststorePath); + } + } + + SSLUtil serverSSLUtil = new SSLUtil(new KeyStoreKeyManager(keystoreFile.toFile(), "changeit".toCharArray()), + null); + InMemoryListenerConfig sslListenerConfig = InMemoryListenerConfig.createLDAPSConfig("ldaps", + InetAddress.getLoopbackAddress(), 0, serverSSLUtil.createSSLServerSocketFactory(), + null); + dsConfig.setListenerConfigs(listenerConfig, sslListenerConfig); + ldapServer = new InMemoryDirectoryServer(dsConfig); + + // Load the LDIF file from the Camel LDAP tests + LDIFReader ldifReader = new LDIFReader( + LdapTest.class.getClassLoader().getResourceAsStream("LdapRouteTest.ldif")); + ldapServer.importFromLDIF(true, ldifReader); + ldapServer.startListening(); + + String host = ldapServer.getListenAddress("ldap").getHostAddress(); + int port = ldapServer.getListenPort("ldap"); + int sslPort = ldapServer.getListenPort("ldaps"); + + return Map.of( + "ldap.host", host, + "ldap.port", port + "", + "ldap.sslPort", sslPort + ""); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + @Override + public void stop() { + if (ldapServer != null) { + try { + ldapServer.close(); + } catch (Exception e) { + // ignored + } + } + } +}