This is an automated email from the ASF dual-hosted git repository.
aldettinger 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 aaa10f1df9 Updates to LDAP tests and usage docs (#5310)
aaa10f1df9 is described below
commit aaa10f1df9067c3aeb8c7afe45fff7bd551e7fcb
Author: Darren Coleman <[email protected]>
AuthorDate: Mon Sep 18 12:21:36 2023 +0100
Updates to LDAP tests and usage docs (#5310)
Fixes #5309
---
.../ROOT/pages/reference/extensions/ldap.adoc | 136 +--------------------
extensions/ldap/runtime/src/main/doc/usage.adoc | 134 +-------------------
.../quarkus/component/ldap/it/LdapResource.java | 48 +++++---
.../camel/quarkus/component/ldap/it/LdapTest.java | 28 ++++-
4 files changed, 60 insertions(+), 286 deletions(-)
diff --git a/docs/modules/ROOT/pages/reference/extensions/ldap.adoc
b/docs/modules/ROOT/pages/reference/extensions/ldap.adoc
index f0880d6d3d..8194a85386 100644
--- a/docs/modules/ROOT/pages/reference/extensions/ldap.adoc
+++ b/docs/modules/ROOT/pages/reference/extensions/ldap.adoc
@@ -46,146 +46,16 @@ endif::[]
[id="extensions-ldap-usage"]
== Usage
-[id="extensions-ldap-usage-dircontext"]
-=== DirContext
-
-The URI, `ldap:ldapserver`, references a bean with the ID `ldapserver`. A CDI
producer method may be used to instantiate a `DirContext` object as follows:
-
-[source,java]
-----
-public class LdapServerProducer {
-
- @Produces
- @Dependent
- @Named("ldapserver")
- public DirContext createLdapServer() throws Exception {
- Hashtable<String, String> env = new Hashtable<>();
- env.put(Context.INITIAL_CONTEXT_FACTORY,
"com.sun.jndi.ldap.LdapCtxFactory");
- env.put(Context.PROVIDER_URL, "ldap://localhost:10389");
- env.put(Context.SECURITY_AUTHENTICATION, "none");
-
- return new InitialDirContext(env);
- }
-}
-----
-
-The preceding example creates a regular Sun based LDAP `DirContext` that
connects anonymously to a locally hosted LDAP server. The use of the `@Named`
annotation binds the `DirContext` into the Camel registry automatically.
-
-[id="extensions-ldap-usage-configuring-ssl"]
-=== Configuring SSL
-
-When connecting to an LDAP server over SSL/TLS, you may encounter situations
where the default trust manager used by the JVM is unable to verify the
certificate. This can happen, for example, when the server uses a self-signed
certificate or when the certificate is issued by a non-trusted CA. In such
cases, you may need to provide a custom trust manager implementation that can
verify the server's certificate.
-
-The following code shows an implementation of a custom socket factory that can
be used to create SSL/TLS sockets. The class name of the custom SSL socket
factory is then specified in the `java.naming.ldap.factory.socket` property of
the environment hashtable used to create the LDAP context.
-
-[source,java]
-----
-public class CustomSSLSocketFactory extends SSLSocketFactory {
-
- private SSLSocketFactory delegate;
-
- public CustomSSLSocketFactory() throws Exception {
- String trustStoreFilename =
ConfigProvider.getConfig().getValue("ldap.trustStore", String.class);
- String trustStorePassword =
ConfigProvider.getConfig().getValue("ldap.trustStorePassword", String.class);
- KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
- try (InputStream in = new FileInputStream(trustStoreFilename)) {
- keyStore.load(in, trustStorePassword.toCharArray());
- }
- TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
- tmf.init(keyStore);
- SSLContext ctx = SSLContext.getInstance("TLS");
- ctx.init(null, tmf.getTrustManagers(), null);
- delegate = ctx.getSocketFactory();
-
- }
-
- public static SocketFactory getDefault() {
- try {
- return new CustomSSLSocketFactory();
- } catch (Exception ex) {
- ex.printStackTrace();
- return null;
- }
- }
-
- @Override
- public Socket createSocket(Socket s, String host, int port, boolean
autoClose) throws IOException {
- return delegate.createSocket(s, host, port, autoClose);
- }
-
- @Override
- public String[] getDefaultCipherSuites() {
- return delegate.getDefaultCipherSuites();
- }
-
- @Override
- public String[] getSupportedCipherSuites() {
- return delegate.getSupportedCipherSuites();
- }
-
- @Override
- public Socket createSocket(String host, int port) throws IOException,
UnknownHostException {
- return delegate.createSocket(host, port);
- }
-
- @Override
- public Socket createSocket(InetAddress address, int port) throws
IOException {
- return delegate.createSocket(address, port);
- }
-
- @Override
- public Socket createSocket(String host, int port, InetAddress
localAddress, int localPort)
- throws IOException, UnknownHostException {
- return delegate.createSocket(host, port, localAddress, localPort);
- }
-
- @Override
- public Socket createSocket(InetAddress address, int port, InetAddress
localAddress, int localPort)
- throws IOException {
- return delegate.createSocket(address, port, localAddress, localPort);
- }
-}
-----
-
-The constructor uses the `ConfigProvider` to read the `ldap.trustStore` and
`ldap.trustStorePassword` configuration properties, which could be specified in
the `application.properties` file as follows:
-
-[source,properties]
-----
-ldap.trustStore=/path/to/truststore.jks
-ldap.trustStorePassword=secret
-----
-
-Finally, alter the `LdapServerProducer.createLdapServer()` method so that the
`PROVIDER_URL` entry uses the `ldaps` protocol instead of `ldap`, and add the
`CustomSSLSocketFactory` entry:
-
-[source,java]
-----
-public class LdapServerProducer {
-
- @Produces
- @Dependent
- @Named("ldapserver")
- public DirContext createLdapServer() throws Exception {
- Hashtable<String, String> env = new Hashtable<>();
- env.put(Context.INITIAL_CONTEXT_FACTORY,
"com.sun.jndi.ldap.LdapCtxFactory");
- env.put(Context.PROVIDER_URL, "ldaps://" +
InetAddress.getLocalHost().getCanonicalHostName() + ":10636");
- env.put(Context.SECURITY_AUTHENTICATION, "none");
- env.put("java.naming.ldap.factory.socket",
CustomSSLSocketFactory.class.getName());
-
- return new InitialDirContext(env);
- }
-}
-----
-
[id="extensions-ldap-usage-using-ssl-in-native-mode"]
-==== Using SSL in Native Mode
+=== Using SSL in Native Mode
-When using a custom `SSLSocketFactory` in native mode, 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:
+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:
[source,java]
----
@RegisterForReflection
public class CustomSSLSocketFactory extends SSLSocketFactory {
- // The class definition is the same as above.
+ // The class definition is the same as in the above link.
}
----
diff --git a/extensions/ldap/runtime/src/main/doc/usage.adoc
b/extensions/ldap/runtime/src/main/doc/usage.adoc
index ffbc96e7c5..dbb52638a8 100644
--- a/extensions/ldap/runtime/src/main/doc/usage.adoc
+++ b/extensions/ldap/runtime/src/main/doc/usage.adoc
@@ -1,139 +1,11 @@
-=== DirContext
+=== Using SSL in Native Mode
-The URI, `ldap:ldapserver`, references a bean with the ID `ldapserver`. A CDI
producer method may be used to instantiate a `DirContext` object as follows:
-
-[source,java]
-----
-public class LdapServerProducer {
-
- @Produces
- @Dependent
- @Named("ldapserver")
- public DirContext createLdapServer() throws Exception {
- Hashtable<String, String> env = new Hashtable<>();
- env.put(Context.INITIAL_CONTEXT_FACTORY,
"com.sun.jndi.ldap.LdapCtxFactory");
- env.put(Context.PROVIDER_URL, "ldap://localhost:10389");
- env.put(Context.SECURITY_AUTHENTICATION, "none");
-
- return new InitialDirContext(env);
- }
-}
-----
-
-The preceding example creates a regular Sun based LDAP `DirContext` that
connects anonymously to a locally hosted LDAP server. The use of the `@Named`
annotation binds the `DirContext` into the Camel registry automatically.
-
-=== Configuring SSL
-
-When connecting to an LDAP server over SSL/TLS, you may encounter situations
where the default trust manager used by the JVM is unable to verify the
certificate. This can happen, for example, when the server uses a self-signed
certificate or when the certificate is issued by a non-trusted CA. In such
cases, you may need to provide a custom trust manager implementation that can
verify the server's certificate.
-
-The following code shows an implementation of a custom socket factory that can
be used to create SSL/TLS sockets. The class name of the custom SSL socket
factory is then specified in the `java.naming.ldap.factory.socket` property of
the environment hashtable used to create the LDAP context.
-
-[source,java]
-----
-public class CustomSSLSocketFactory extends SSLSocketFactory {
-
- private SSLSocketFactory delegate;
-
- public CustomSSLSocketFactory() throws Exception {
- String trustStoreFilename =
ConfigProvider.getConfig().getValue("ldap.trustStore", String.class);
- String trustStorePassword =
ConfigProvider.getConfig().getValue("ldap.trustStorePassword", String.class);
- KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
- try (InputStream in = new FileInputStream(trustStoreFilename)) {
- keyStore.load(in, trustStorePassword.toCharArray());
- }
- TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
- tmf.init(keyStore);
- SSLContext ctx = SSLContext.getInstance("TLS");
- ctx.init(null, tmf.getTrustManagers(), null);
- delegate = ctx.getSocketFactory();
-
- }
-
- public static SocketFactory getDefault() {
- try {
- return new CustomSSLSocketFactory();
- } catch (Exception ex) {
- ex.printStackTrace();
- return null;
- }
- }
-
- @Override
- public Socket createSocket(Socket s, String host, int port, boolean
autoClose) throws IOException {
- return delegate.createSocket(s, host, port, autoClose);
- }
-
- @Override
- public String[] getDefaultCipherSuites() {
- return delegate.getDefaultCipherSuites();
- }
-
- @Override
- public String[] getSupportedCipherSuites() {
- return delegate.getSupportedCipherSuites();
- }
-
- @Override
- public Socket createSocket(String host, int port) throws IOException,
UnknownHostException {
- return delegate.createSocket(host, port);
- }
-
- @Override
- public Socket createSocket(InetAddress address, int port) throws
IOException {
- return delegate.createSocket(address, port);
- }
-
- @Override
- public Socket createSocket(String host, int port, InetAddress
localAddress, int localPort)
- throws IOException, UnknownHostException {
- return delegate.createSocket(host, port, localAddress, localPort);
- }
-
- @Override
- public Socket createSocket(InetAddress address, int port, InetAddress
localAddress, int localPort)
- throws IOException {
- return delegate.createSocket(address, port, localAddress, localPort);
- }
-}
-----
-
-The constructor uses the `ConfigProvider` to read the `ldap.trustStore` and
`ldap.trustStorePassword` configuration properties, which could be specified in
the `application.properties` file as follows:
-
-[source,properties]
-----
-ldap.trustStore=/path/to/truststore.jks
-ldap.trustStorePassword=secret
-----
-
-Finally, alter the `LdapServerProducer.createLdapServer()` method so that the
`PROVIDER_URL` entry uses the `ldaps` protocol instead of `ldap`, and add the
`CustomSSLSocketFactory` entry:
-
-[source,java]
-----
-public class LdapServerProducer {
-
- @Produces
- @Dependent
- @Named("ldapserver")
- public DirContext createLdapServer() throws Exception {
- Hashtable<String, String> env = new Hashtable<>();
- env.put(Context.INITIAL_CONTEXT_FACTORY,
"com.sun.jndi.ldap.LdapCtxFactory");
- env.put(Context.PROVIDER_URL, "ldaps://" +
InetAddress.getLocalHost().getCanonicalHostName() + ":10636");
- env.put(Context.SECURITY_AUTHENTICATION, "none");
- env.put("java.naming.ldap.factory.socket",
CustomSSLSocketFactory.class.getName());
-
- return new InitialDirContext(env);
- }
-}
-----
-
-==== Using SSL in Native Mode
-
-When using a custom `SSLSocketFactory` in native mode, 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:
+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:
[source,java]
----
@RegisterForReflection
public class CustomSSLSocketFactory extends SSLSocketFactory {
- // The class definition is the same as above.
+ // The class definition is the same as in the above link.
}
----
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 746524857b..73828a6c02 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
@@ -30,7 +30,9 @@ 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;
@@ -50,6 +52,12 @@ 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.
@@ -62,43 +70,43 @@ public class LdapResource {
@POST
@Consumes(MediaType.APPLICATION_JSON)
public void configure(Map<String, String> options) throws Exception {
- String host = options.get("host");
- String port = options.get("port");
- boolean useSSL = Boolean.valueOf(options.get("ssl"));
- String trustStoreFilename = options.get("trustStore");
- String trustStorePassword = options.get("trustStorePassword");
-
- DirContext dirContext = createLdapContext(host, port, useSSL,
trustStoreFilename, trustStorePassword);
- camelContext.getRegistry().bind("ldapserver", dirContext);
+ ldapHost = options.get("host");
+ ldapPort = options.get("port");
+ useSSL = Boolean.valueOf(options.get("ssl"));
+ trustStoreFilename = options.get("trustStore");
+ trustStorePassword = options.get("trustStorePassword");
}
@Path("/search")
@GET
@Produces(MediaType.APPLICATION_JSON)
- public Response search(@QueryParam("q") String filter) throws Exception {
- ProducerTemplate producer = camelContext.createProducerTemplate();
- List<SearchResult> results = producer.requestBody("direct:start",
filter, List.class);
- return Response.ok(convertSearchResults(results)).build();
+ public Response search(@QueryParam("q") String q) throws Exception {
+ return Response.ok(searchByUid(q)).build();
}
@Path("/safeSearch")
@GET
@Produces(MediaType.APPLICATION_JSON)
- public Response safeSearch(@QueryParam("q") String unsafeFilter) throws
Exception {
- String filter = String.format("(ou=%s)",
LdapHelper.escapeFilter(unsafeFilter));
+ public Response safeSearch(@QueryParam("q") String q) throws Exception {
+ return Response.ok(searchByUid(LdapHelper.escapeFilter(q))).build();
+ }
+
+ @SuppressWarnings("unchecked")
+ private List<Map<String, String>> searchByUid(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);
-
- return Response.ok(convertSearchResults(results)).build();
+ return convertSearchResults(results);
}
- private DirContext createLdapContext(String host, String port, boolean
useSSL, String trustStoreFilename,
- String trustStorePassword)
- throws Exception {
+ @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,
host, port));
+ env.put(Context.PROVIDER_URL, String.format("%s://%s:%s", scheme,
ldapHost, ldapPort));
env.put(Context.SECURITY_AUTHENTICATION, "none");
if (useSSL) {
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 672a437f82..962ec3b7c7 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
@@ -24,6 +24,7 @@ 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;
@@ -110,7 +111,7 @@ class LdapTest {
TypeRef<List<Map<String, Object>>> typeRef = new TypeRef<>() {
};
List<Map<String, Object>> results = RestAssured.given()
- .queryParam("q", "(uid=tcruise)")
+ .queryParam("q", "tcruise")
.get("/ldap/search")
.then()
.statusCode(200)
@@ -132,14 +133,37 @@ class LdapTest {
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("q", "test*")
- .get("/ldap/safeSearch")
+ .get("/ldap/search")
.then()
.statusCode(200)
.extract().as(typeRef);
+ assertEquals(3, results.size());
+ assertEquals(List.of("test1", "test2", "testNoOU"),
+ results.stream().map(r ->
r.get("uid")).collect(Collectors.toList()));
+ // Verify that the same query passed to the safeSearch returns no
matching results.
+ results = RestAssured.given()
+ .queryParam("q", "test*")
+ .get("/ldap/safeSearch")
+ .then()
+ .statusCode(200)
+ .extract().as(typeRef);
assertEquals(0, results.size());
+
+ // Verify that non-escaped queries also work with escaped search
+ results = RestAssured.given()
+ .queryParam("q", "test1")
+ .get("/ldap/safeSearch")
+ .then()
+ .statusCode(200)
+ .extract().as(typeRef);
+ assertEquals(1, results.size());
+ assertEquals("test1", results.get(0).get("ou"));
+
}
/**