This is an automated email from the ASF dual-hosted git repository.
jungm pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tomee.git
The following commit(s) were added to refs/heads/main by this push:
new 2e4ed54f3a keep client failover and redirect locations on the
configured transport scheme
2e4ed54f3a is described below
commit 2e4ed54f3a6cce80cc4ae82dfdf62e2752b021a3
Author: Markus Jung <[email protected]>
AuthorDate: Sun Aug 23 21:32:37 2026 +0200
keep client failover and redirect locations on the configured transport
scheme
---
.../java/org/apache/openejb/client/Client.java | 24 +++++--
.../org/apache/openejb/client/JNDIContext.java | 8 ++-
.../org/apache/openejb/client/ServerMetaData.java | 6 +-
.../openejb/client/TransportSecurityPolicy.java | 81 ++++++++++++++++++++++
.../client/TransportSecurityPolicyTest.java | 81 ++++++++++++++++++++++
5 files changed, 192 insertions(+), 8 deletions(-)
diff --git
a/server/openejb-client/src/main/java/org/apache/openejb/client/Client.java
b/server/openejb-client/src/main/java/org/apache/openejb/client/Client.java
index 8d19769722..2e19618e3d 100644
--- a/server/openejb-client/src/main/java/org/apache/openejb/client/Client.java
+++ b/server/openejb-client/src/main/java/org/apache/openejb/client/Client.java
@@ -506,18 +506,30 @@ public class Client {
throw new IllegalArgumentException("clusterMetaData cannot be
null");
}
+ ClusterMetaData accepted = updated;
+ final URI[] filtered =
TransportSecurityPolicy.filter(serverMetaData.getLocation(),
updated.getLocations());
+ if (filtered != updated.getLocations()) {
+ if (filtered.length == 0) {
+ logger.log(Level.WARNING, "Ignoring cluster update: no
pushed location preserves the transport security of "
+ + serverMetaData.getLocation() + ". Set -D" +
TransportSecurityPolicy.ALLOW_DOWNGRADE + "=true to allow it.");
+ return;
+ }
+ accepted = new ClusterMetaData(updated.getVersion(), filtered);
+
accepted.setConnectionStrategy(updated.getConnectionStrategy());
+ }
+
final ClusterMetaData previous = this.clusterMetaData;
- this.clusterMetaData = updated;
+ this.clusterMetaData = accepted;
- if (updated.getConnectionStrategy() == null) {
-
updated.setConnectionStrategy(previous.getConnectionStrategy());
+ if (accepted.getConnectionStrategy() == null) {
+
accepted.setConnectionStrategy(previous.getConnectionStrategy());
}
- updated.setLastLocation(previous.getLastLocation());
- final ClusterMetaDataUpdated clusterMetaDataUpdated = new
ClusterMetaDataUpdated(serverMetaData, updated, previous);
+ accepted.setLastLocation(previous.getLastLocation());
+ final ClusterMetaDataUpdated clusterMetaDataUpdated = new
ClusterMetaDataUpdated(serverMetaData, accepted, previous);
fireEvent(clusterMetaDataUpdated);
- final Set<URI> found = locations(updated);
+ final Set<URI> found = locations(accepted);
final Set<URI> existing = locations(previous);
for (final URI uri : diff(existing, found)) {
diff --git
a/server/openejb-client/src/main/java/org/apache/openejb/client/JNDIContext.java
b/server/openejb-client/src/main/java/org/apache/openejb/client/JNDIContext.java
index 4159ae773a..51fe24fbcb 100644
---
a/server/openejb-client/src/main/java/org/apache/openejb/client/JNDIContext.java
+++
b/server/openejb-client/src/main/java/org/apache/openejb/client/JNDIContext.java
@@ -386,7 +386,13 @@ public class JNDIContext implements InitialContextFactory,
Context {
break;
case ResponseCodes.AUTH_REDIRECT:
client = res.getIdentity();
- server = res.getServer();
+ final ServerMetaData redirect = res.getServer();
+ if (redirect != null &&
!TransportSecurityPolicy.accepts(server.getLocation(), redirect.getLocation()))
{
+ throw new AuthenticationException("Refusing authentication
redirect to " + redirect.getLocation()
+ + ": it would downgrade the transport security of " +
server.getLocation()
+ + ". Set -D" + TransportSecurityPolicy.ALLOW_DOWNGRADE
+ "=true to allow it.");
+ }
+ server = redirect;
break;
case ResponseCodes.AUTH_DENIED:
throw (AuthenticationException) new
AuthenticationException("This principle is not
authorized.").initCause(res.getDeniedCause());
diff --git
a/server/openejb-client/src/main/java/org/apache/openejb/client/ServerMetaData.java
b/server/openejb-client/src/main/java/org/apache/openejb/client/ServerMetaData.java
index bb9e36d3af..69d993d458 100644
---
a/server/openejb-client/src/main/java/org/apache/openejb/client/ServerMetaData.java
+++
b/server/openejb-client/src/main/java/org/apache/openejb/client/ServerMetaData.java
@@ -43,7 +43,11 @@ public class ServerMetaData implements Externalizable {
}
public void merge(final ServerMetaData toMerge) {
- locations = toMerge.locations;
+ final URI[] filtered = TransportSecurityPolicy.filter(location,
toMerge.locations);
+ if (filtered != null && filtered.length == 0) {
+ return;
+ }
+ locations = filtered;
}
public URI getLocation() {
diff --git
a/server/openejb-client/src/main/java/org/apache/openejb/client/TransportSecurityPolicy.java
b/server/openejb-client/src/main/java/org/apache/openejb/client/TransportSecurityPolicy.java
new file mode 100644
index 0000000000..f9daa6187c
--- /dev/null
+++
b/server/openejb-client/src/main/java/org/apache/openejb/client/TransportSecurityPolicy.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.openejb.client;
+
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Keeps server-pushed locations (cluster updates, failover lists,
+ * authentication redirects) on a transport at least as protected as the one
+ * the client was configured with: a client using a TLS scheme
+ * (ejbds/zejbds/https) only follows pushed locations with an equally
+ * protected scheme unless {@link #ALLOW_DOWNGRADE} is set.
+ */
+final class TransportSecurityPolicy {
+
+ public static final String ALLOW_DOWNGRADE =
"openejb.client.allowTransportDowngrade";
+
+ private static final Logger LOGGER = Logger.getLogger("OpenEJB.client");
+
+ private TransportSecurityPolicy() {
+ // utility class
+ }
+
+ static boolean isSecure(final URI uri) {
+ if (uri == null) {
+ return false;
+ }
+ final String scheme = uri.getScheme();
+ return "ejbds".equalsIgnoreCase(scheme) ||
"zejbds".equalsIgnoreCase(scheme) || "https".equalsIgnoreCase(scheme);
+ }
+
+ static boolean accepts(final URI baseline, final URI pushed) {
+ return !isSecure(baseline) || isSecure(pushed) || allowDowngrade();
+ }
+
+ /**
+ * @return the pushed array untouched when no filtering applies, else a new
+ * array retaining only the locations at least as protected as the baseline
+ */
+ static URI[] filter(final URI baseline, final URI[] pushed) {
+ if (pushed == null || !isSecure(baseline) || allowDowngrade()) {
+ return pushed;
+ }
+ final List<URI> accepted = new ArrayList<>(pushed.length);
+ for (final URI uri : pushed) {
+ if (isSecure(uri)) {
+ accepted.add(uri);
+ } else {
+ LOGGER.log(Level.WARNING, "Ignoring server-pushed location " +
uri
+ + ": it would downgrade the transport security of " +
baseline
+ + ". Set -D" + ALLOW_DOWNGRADE + "=true to allow it.");
+ }
+ }
+ if (accepted.size() == pushed.length) {
+ return pushed;
+ }
+ return accepted.toArray(new URI[0]);
+ }
+
+ private static boolean allowDowngrade() {
+ return Boolean.getBoolean(ALLOW_DOWNGRADE);
+ }
+}
diff --git
a/server/openejb-client/src/test/java/org/apache/openejb/client/TransportSecurityPolicyTest.java
b/server/openejb-client/src/test/java/org/apache/openejb/client/TransportSecurityPolicyTest.java
new file mode 100644
index 0000000000..790299bae3
--- /dev/null
+++
b/server/openejb-client/src/test/java/org/apache/openejb/client/TransportSecurityPolicyTest.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.openejb.client;
+
+import org.junit.Test;
+
+import java.net.URI;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+
+public class TransportSecurityPolicyTest {
+
+ @Test
+ public void plaintextLocationsAreDroppedForSecureBaseline() {
+ final URI baseline = URI.create("ejbds://app1:4203");
+ final URI[] pushed = {
+ URI.create("ejbd://app2:4201"),
+ URI.create("ejbds://app3:4203"),
+ URI.create("http://app4:80/ejb"),
+ };
+ final URI[] filtered = TransportSecurityPolicy.filter(baseline,
pushed);
+ assertArrayEquals(new URI[]{URI.create("ejbds://app3:4203")},
filtered);
+ }
+
+ @Test
+ public void plaintextBaselineIsNotFiltered() {
+ final URI baseline = URI.create("ejbd://app1:4201");
+ final URI[] pushed = {URI.create("ejbd://app2:4201"),
URI.create("ejbds://app3:4203")};
+ assertSame(pushed, TransportSecurityPolicy.filter(baseline, pushed));
+ }
+
+ @Test
+ public void accepts() {
+
assertTrue(TransportSecurityPolicy.accepts(URI.create("ejbds://a:4203"),
URI.create("https://b:8443")));
+
assertTrue(TransportSecurityPolicy.accepts(URI.create("ejbd://a:4201"),
URI.create("ejbd://b:4201")));
+
assertFalse(TransportSecurityPolicy.accepts(URI.create("ejbds://a:4203"),
URI.create("ejbd://b:4201")));
+
assertFalse(TransportSecurityPolicy.accepts(URI.create("zejbds://a:4203"),
URI.create("zejbd://b:4201")));
+ }
+
+ @Test
+ public void clusterUpdateKeepsTransportOfSecureBaseline() {
+ final ServerMetaData server = new
ServerMetaData(URI.create("ejbds://transport-test-1:4203"));
+ final Client.Context context = Client.getContext(server);
+
+ context.setClusterMetaData(new ClusterMetaData(2,
URI.create("ejbd://member1:4201")));
+ assertArrayEquals(new
URI[]{URI.create("ejbds://transport-test-1:4203")},
+ context.getClusterMetaData().getLocations());
+
+ context.setClusterMetaData(new ClusterMetaData(3,
+ URI.create("ejbd://member1:4201"),
URI.create("ejbds://member2:4203")));
+ assertArrayEquals(new URI[]{URI.create("ejbds://member2:4203")},
+ context.getClusterMetaData().getLocations());
+ }
+
+ @Test
+ public void mergeKeepsTransportOfSecureBaseline() {
+ final ServerMetaData server = new
ServerMetaData(URI.create("ejbds://transport-test-2:4203"));
+ server.merge(new ServerMetaData(URI.create("ejbd://member1:4201")));
+ assertArrayEquals(new
URI[]{URI.create("ejbds://transport-test-2:4203")}, server.getLocations());
+
+ server.merge(new ServerMetaData(URI.create("ejbds://member2:4203"),
URI.create("ejbd://member1:4201")));
+ assertArrayEquals(new URI[]{URI.create("ejbds://member2:4203")},
server.getLocations());
+ }
+}