This is an automated email from the ASF dual-hosted git repository. rzo1 pushed a commit to branch tomee-10.x in repository https://gitbox.apache.org/repos/asf/tomee.git
commit 7dfbcd40faac45abe9823c4261f424b6f39a4974 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 (cherry picked from commit 2e4ed54f3a6cce80cc4ae82dfdf62e2752b021a3) --- .../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()); + } +}
