This is an automated email from the ASF dual-hosted git repository.
vavrtom pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/qpid-broker-j.git
The following commit(s) were added to refs/heads/main by this push:
new c88188678f QPID-8648: [Broker-J] Allow for max frame size >4096 before
Open frame (SASL) (#234)
c88188678f is described below
commit c88188678fb1e9b804cacf702507b28bbbf9d105
Author: Daniil Kirilyuk <[email protected]>
AuthorDate: Mon Jan 29 13:17:17 2024 +0100
QPID-8648: [Broker-J] Allow for max frame size >4096 before Open frame
(SASL) (#234)
---
.../protocol/v1_0/AMQPConnection_1_0Impl.java | 4 +-
.../v1_0/transport/security/sasl/SaslTest.java | 37 ++++
systests/systests-utils/pom.xml | 4 +
.../qpid/tests/utils/AddOAuth2MockProvider.java | 32 ++++
.../utils/EmbeddedBrokerPerClassAdminImpl.java | 203 +++++++++++++++++----
.../qpid/tests/utils/OAuth2MockEndpoint.java | 134 ++++++++++++++
.../qpid/tests/utils/OAuth2MockEndpointHolder.java | 180 ++++++++++++++++++
7 files changed, 553 insertions(+), 41 deletions(-)
diff --git
a/broker-plugins/amqp-1-0-protocol/src/main/java/org/apache/qpid/server/protocol/v1_0/AMQPConnection_1_0Impl.java
b/broker-plugins/amqp-1-0-protocol/src/main/java/org/apache/qpid/server/protocol/v1_0/AMQPConnection_1_0Impl.java
index 15ccb3312f..2bd689ef6f 100644
---
a/broker-plugins/amqp-1-0-protocol/src/main/java/org/apache/qpid/server/protocol/v1_0/AMQPConnection_1_0Impl.java
+++
b/broker-plugins/amqp-1-0-protocol/src/main/java/org/apache/qpid/server/protocol/v1_0/AMQPConnection_1_0Impl.java
@@ -185,7 +185,7 @@ public class AMQPConnection_1_0Impl extends
AbstractAMQPConnection<AMQPConnectio
private final SubjectCreator _subjectCreator;
private int _channelMax = 0;
- private int _maxFrameSize = 4096;
+ private int _maxFrameSize;
private String _remoteContainerId;
private SocketAddress _remoteAddress;
@@ -262,6 +262,8 @@ public class AMQPConnection_1_0Impl extends
AbstractAMQPConnection<AMQPConnectio
_incomingIdleTimeout = 1000L * port.getHeartbeatDelay();
_frameWriter = new FrameWriter(getDescribedTypeRegistry(),
getSender());
+
+ _maxFrameSize = getBroker().getNetworkBufferSize();
}
@Override
diff --git
a/systests/protocol-tests-amqp-1-0/src/test/java/org/apache/qpid/tests/protocol/v1_0/transport/security/sasl/SaslTest.java
b/systests/protocol-tests-amqp-1-0/src/test/java/org/apache/qpid/tests/protocol/v1_0/transport/security/sasl/SaslTest.java
index f903a3ac21..9ef19e8ae1 100644
---
a/systests/protocol-tests-amqp-1-0/src/test/java/org/apache/qpid/tests/protocol/v1_0/transport/security/sasl/SaslTest.java
+++
b/systests/protocol-tests-amqp-1-0/src/test/java/org/apache/qpid/tests/protocol/v1_0/transport/security/sasl/SaslTest.java
@@ -45,6 +45,7 @@ import
org.apache.qpid.server.protocol.v1_0.type.transport.Open;
import org.apache.qpid.tests.protocol.SpecificationTest;
import org.apache.qpid.tests.protocol.v1_0.FrameTransport;
import org.apache.qpid.tests.protocol.v1_0.Interaction;
+import org.apache.qpid.tests.utils.AddOAuth2MockProvider;
import org.apache.qpid.tests.utils.BrokerAdmin;
import org.apache.qpid.tests.utils.BrokerAdminUsingTestBase;
@@ -52,6 +53,7 @@ public class SaslTest extends BrokerAdminUsingTestBase
{
private static final Symbol CRAM_MD5 = Symbol.getSymbol("CRAM-MD5");
private static final Symbol PLAIN = Symbol.getSymbol("PLAIN");
+ private static final Symbol OAUTH2 = Symbol.getSymbol("XOAUTH2");
private static final byte[] SASL_AMQP_HEADER_BYTES =
"AMQP\3\1\0\0".getBytes(StandardCharsets.UTF_8);
private static final byte[] AMQP_HEADER_BYTES =
"AMQP\0\1\0\0".getBytes(StandardCharsets.UTF_8);
@@ -329,4 +331,39 @@ public class SaslTest extends BrokerAdminUsingTestBase
transport.assertNoMoreResponsesAndChannelClosed();
}
}
+
+ @Test
+ @AddOAuth2MockProvider()
+ @SpecificationTest(section = "5.3.2", description = "SASL Negotiation")
+ public void veryLongOauth2Token() throws Exception
+ {
+ final byte[] TOKEN = ("user=xxx\1auth=Bearer " + "A".repeat(10_0000) +
"\1host=localhost\1\1")
+ .getBytes(StandardCharsets.US_ASCII);
+
+ try (final FrameTransport transport = new
FrameTransport(getBrokerAdmin(), BrokerAdmin.PortType.AMQP).connect())
+ {
+ final Interaction interaction = transport.newInteraction();
+ final byte[] saslHeaderResponse =
interaction.protocolHeader(SASL_AMQP_HEADER_BYTES)
+ .negotiateProtocol().consumeResponse()
+ .getLatestResponse(byte[].class);
+ assertThat(saslHeaderResponse,
is(equalTo(SASL_AMQP_HEADER_BYTES)));
+
+ final SaslMechanisms saslMechanismsResponse =
interaction.consumeResponse().getLatestResponse(SaslMechanisms.class);
+
assumeTrue(hasItem(OAUTH2).matches(Arrays.asList(saslMechanismsResponse.getSaslServerMechanisms())));
+
+ final Binary initialResponse = new Binary(TOKEN);
+ final SaslOutcome saslOutcome = interaction.saslMechanism(OAUTH2)
+ .saslInitialResponse(initialResponse)
+ .saslInit().consumeResponse()
+ .getLatestResponse(SaslOutcome.class);
+ assertThat(saslOutcome.getCode(), equalTo(SaslCode.OK));
+
+ final byte[] headerResponse =
interaction.protocolHeader(AMQP_HEADER_BYTES)
+ .negotiateProtocol().consumeResponse()
+ .getLatestResponse(byte[].class);
+ assertThat(headerResponse, is(equalTo(AMQP_HEADER_BYTES)));
+
+ transport.assertNoMoreResponses();
+ }
+ }
}
diff --git a/systests/systests-utils/pom.xml b/systests/systests-utils/pom.xml
index fcb72fe20d..d67a5a497d 100644
--- a/systests/systests-utils/pom.xml
+++ b/systests/systests-utils/pom.xml
@@ -61,6 +61,10 @@
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-server</artifactId>
+ </dependency>
</dependencies>
</project>
diff --git
a/systests/systests-utils/src/main/java/org/apache/qpid/tests/utils/AddOAuth2MockProvider.java
b/systests/systests-utils/src/main/java/org/apache/qpid/tests/utils/AddOAuth2MockProvider.java
new file mode 100644
index 0000000000..11f7a7eec6
--- /dev/null
+++
b/systests/systests-utils/src/main/java/org/apache/qpid/tests/utils/AddOAuth2MockProvider.java
@@ -0,0 +1,32 @@
+/*
+ *
+ * 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.qpid.tests.utils;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.METHOD})
+public @interface AddOAuth2MockProvider
+{
+}
diff --git
a/systests/systests-utils/src/main/java/org/apache/qpid/tests/utils/EmbeddedBrokerPerClassAdminImpl.java
b/systests/systests-utils/src/main/java/org/apache/qpid/tests/utils/EmbeddedBrokerPerClassAdminImpl.java
index 03d997f0a3..c995b3ac02 100644
---
a/systests/systests-utils/src/main/java/org/apache/qpid/tests/utils/EmbeddedBrokerPerClassAdminImpl.java
+++
b/systests/systests-utils/src/main/java/org/apache/qpid/tests/utils/EmbeddedBrokerPerClassAdminImpl.java
@@ -24,11 +24,12 @@ import java.io.File;
import java.lang.reflect.Method;
import java.net.InetSocketAddress;
import java.nio.file.Files;
+import java.nio.file.Path;
import java.security.PrivilegedAction;
+import java.security.cert.X509Certificate;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
@@ -38,6 +39,12 @@ import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.X509TrustManager;
import javax.security.auth.Subject;
import com.google.common.util.concurrent.Futures;
@@ -48,6 +55,7 @@ import org.slf4j.LoggerFactory;
import org.apache.qpid.server.SystemLauncher;
import org.apache.qpid.server.SystemLauncherListener;
import
org.apache.qpid.server.logging.logback.LogbackLoggingSystemLauncherListener;
+import org.apache.qpid.server.model.AuthenticationProvider;
import org.apache.qpid.server.model.Broker;
import org.apache.qpid.server.model.ConfiguredObject;
import org.apache.qpid.server.model.Exchange;
@@ -58,20 +66,25 @@ import org.apache.qpid.server.model.Port;
import org.apache.qpid.server.model.Queue;
import org.apache.qpid.server.model.SystemConfig;
import org.apache.qpid.server.model.VirtualHostNode;
+import org.apache.qpid.server.model.port.AmqpPort;
import org.apache.qpid.server.plugin.PluggableService;
import org.apache.qpid.server.security.auth.TaskPrincipal;
+import
org.apache.qpid.server.security.auth.manager.oauth2.cloudfoundry.CloudFoundryOAuth2IdentityResolverService;
import org.apache.qpid.server.store.MemoryConfigurationStore;
import org.apache.qpid.server.util.FileUtils;
import org.apache.qpid.server.virtualhost.QueueManagingVirtualHost;
import org.apache.qpid.server.virtualhostnode.JsonVirtualHostNode;
+import org.apache.qpid.test.utils.tls.TlsResource;
-@SuppressWarnings("unused")
+@SuppressWarnings({"java:S116", "unchecked", "unused"})
+// sonar complains about variable names
@PluggableService
public class EmbeddedBrokerPerClassAdminImpl implements BrokerAdmin
{
private static final Logger LOGGER =
LoggerFactory.getLogger(EmbeddedBrokerPerClassAdminImpl.class);
public static final String TYPE = "EMBEDDED_BROKER_PER_CLASS";
private final Map<String, Integer> _ports = new HashMap<>();
+ private String _tempAuthProvider;
private SystemLauncher _systemLauncher;
private Broker<?> _broker;
private VirtualHostNode<?> _currentVirtualHostNode;
@@ -89,7 +102,8 @@ public class EmbeddedBrokerPerClassAdminImpl implements
BrokerAdmin
_currentWorkDirectory =
Files.createTempDirectory(String.format("qpid-work-%s-%s-", timestamp,
testClass.getSimpleName())).toString();
ConfigItem[] configItems = (ConfigItem[])
testClass.getAnnotationsByType(ConfigItem.class);
- Arrays.stream(configItems).filter(ConfigItem::jvm).forEach(i -> {
+ Arrays.stream(configItems).filter(ConfigItem::jvm).forEach(i ->
+ {
_preservedProperties.put(i.name(),
System.getProperty(i.name()));
System.setProperty(i.name(), i.value());
});
@@ -97,10 +111,8 @@ public class EmbeddedBrokerPerClassAdminImpl implements
BrokerAdmin
context.put("qpid.work_dir", _currentWorkDirectory);
context.put("qpid.port.protocol_handshake_timeout", "1000000");
context.putAll(Arrays.stream(configItems)
- .filter(i -> !i.jvm())
- .collect(Collectors.toMap(ConfigItem::name,
- ConfigItem::value,
- (name, value) ->
value)));
+ .filter(i -> !i.jvm())
+ .collect(Collectors.toMap(ConfigItem::name,
ConfigItem::value, (name, value) -> value)));
Map<String,Object> systemConfigAttributes = new HashMap<>();
systemConfigAttributes.put(ConfiguredObject.CONTEXT, context);
@@ -118,7 +130,7 @@ public class EmbeddedBrokerPerClassAdminImpl implements
BrokerAdmin
systemLauncherListeners.add(new
LogbackLoggingSystemLauncherListener());
systemLauncherListeners.add(new
ShutdownLoggingSystemLauncherListener());
systemLauncherListeners.add(new PortExtractingLauncherListener());
- _systemLauncher = new
SystemLauncher(systemLauncherListeners.toArray(new
SystemLauncherListener[systemLauncherListeners.size()]));
+ _systemLauncher = new
SystemLauncher(systemLauncherListeners.toArray(new SystemLauncherListener[0]));
_systemLauncher.startup(systemConfigAttributes);
}
@@ -148,9 +160,9 @@ public class EmbeddedBrokerPerClassAdminImpl implements
BrokerAdmin
String blueprint =
System.getProperty("virtualhostnode.context.blueprint");
Map<String, Object> attributes = new HashMap<>();
- attributes.put(VirtualHostNode.NAME, virtualHostNodeName);
- attributes.put(VirtualHostNode.TYPE, storeType);
- attributes.put(VirtualHostNode.CONTEXT,
Collections.singletonMap("virtualhostBlueprint", blueprint));
+ attributes.put(ConfiguredObject.NAME, virtualHostNodeName);
+ attributes.put(ConfiguredObject.TYPE, storeType);
+ attributes.put(ConfiguredObject.CONTEXT,
Collections.singletonMap("virtualhostBlueprint", blueprint));
attributes.put(VirtualHostNode.DEFAULT_VIRTUAL_HOST_NODE, true);
attributes.put(VirtualHostNode.VIRTUALHOST_INITIAL_CONFIGURATION,
blueprint);
if (storeDir != null)
@@ -158,8 +170,12 @@ public class EmbeddedBrokerPerClassAdminImpl implements
BrokerAdmin
attributes.put(JsonVirtualHostNode.STORE_PATH, storeDir);
}
- _currentVirtualHostNode = _broker.createChild(VirtualHostNode.class,
attributes);
+ if (method.getAnnotation(AddOAuth2MockProvider.class) != null)
+ {
+ createOAuth2AuthenticationManager();
+ }
+ _currentVirtualHostNode = _broker.createChild(VirtualHostNode.class,
attributes);
}
@Override
@@ -182,6 +198,16 @@ public class EmbeddedBrokerPerClassAdminImpl implements
BrokerAdmin
}
return null;
});
+
+ if (method.getAnnotation(AddOAuth2MockProvider.class) != null)
+ {
+ final AmqpPort<?> port = (AmqpPort<?>)
_broker.getPorts().stream().filter(p -> "AMQP".equals(p.getName()))
+ .findFirst().orElse(null);
+ if (port != null)
+ {
+ port.setAttributes(Map.of(Port.AUTHENTICATION_PROVIDER,
_tempAuthProvider));
+ }
+ }
}
@Override
@@ -221,10 +247,10 @@ public class EmbeddedBrokerPerClassAdminImpl implements
BrokerAdmin
public void createQueue(final String queueName)
{
final Map<String, Object> attributes = new HashMap<>();
- attributes.put(Queue.NAME, queueName);
- attributes.put(Queue.TYPE, "standard");
- final Queue queue =
_currentVirtualHostNode.getVirtualHost().createChild(Queue.class, attributes);
- final Exchange exchange =
_currentVirtualHostNode.getVirtualHost().getChildByName(Exchange.class,
"amq.direct");
+ attributes.put(ConfiguredObject.NAME, queueName);
+ attributes.put(ConfiguredObject.TYPE, "standard");
+ final Queue<?> queue =
_currentVirtualHostNode.getVirtualHost().createChild(Queue.class, attributes);
+ final Exchange<?> exchange =
_currentVirtualHostNode.getVirtualHost().getChildByName(Exchange.class,
"amq.direct");
exchange.bind(queueName, queueName, Collections.emptyMap(), false);
}
@@ -310,7 +336,7 @@ public class EmbeddedBrokerPerClassAdminImpl implements
BrokerAdmin
@Override
public Map<String, Object> getHeaders()
{
- return null;
+ return Map.of();
}
@Override
@@ -332,7 +358,7 @@ public class EmbeddedBrokerPerClassAdminImpl implements
BrokerAdmin
@Override
public int getQueueDepthMessages(final String testQueueName)
{
- Queue queue =
_currentVirtualHostNode.getVirtualHost().getChildByName(Queue.class,
testQueueName);
+ Queue<?> queue =
_currentVirtualHostNode.getVirtualHost().getChildByName(Queue.class,
testQueueName);
return queue.getQueueDepthMessages();
}
@@ -432,17 +458,89 @@ public class EmbeddedBrokerPerClassAdminImpl implements
BrokerAdmin
return TYPE;
}
- private Queue getQueue(final String queueName)
+ private Queue<?> getQueue(final String queueName)
{
- Collection<Queue> queues =
_currentVirtualHostNode.getVirtualHost().getChildren(Queue.class);
- for (Queue queue : queues)
+ return
_currentVirtualHostNode.getVirtualHost().getChildren(Queue.class).stream()
+ .filter(queue -> queue.getName().equals(queueName))
+ .findFirst()
+ .orElseThrow(() -> new NotFoundException(String.format("Queue
'%s' not found", queueName)));
+ }
+
+ private void createOAuth2AuthenticationManager()
+ {
+ final String TEST_CLIENT_ID = "testClientId";
+ final String TEST_CLIENT_SECRET = "testClientSecret";
+ final String TEST_IDENTITY_RESOLVER_TYPE =
CloudFoundryOAuth2IdentityResolverService.TYPE;
+ final String TEST_URI_PATTERN = "https://%s:%d%s";
+ final String TEST_AUTHORIZATION_ENDPOINT_NEEDS_AUTH = "true";
+ final String TEST_SCOPE = "testScope";
+ final String TEST_TRUST_STORE_NAME = null;
+ final String TEST_ENDPOINT_HOST = "localhost";
+ final String TEST_AUTHORIZATION_ENDPOINT_PATH = "/testauth";
+ final String TEST_TOKEN_ENDPOINT_PATH = "/testtoken";
+ final String TEST_IDENTITY_RESOLVER_ENDPOINT_PATH = "/testidresolver";
+ final String TEST_POST_LOGOUT_PATH = "/testpostlogout";
+
+ OAuth2MockEndpointHolder server;
+ try
+ {
+ final TlsResource tlsResource = new TlsResource();
+ tlsResource.beforeAll(null);
+ final Path keyStore =
tlsResource.createSelfSignedKeyStore("CN=127.0.0.1");
+ server = new
OAuth2MockEndpointHolder(keyStore.toFile().getAbsolutePath(),
tlsResource.getSecret(), tlsResource.getKeyStoreType());
+ final OAuth2MockEndpoint identityResolverEndpoint = new
OAuth2MockEndpoint();
+ identityResolverEndpoint.putExpectedParameter("token",
"A".repeat(10_0000));
+ identityResolverEndpoint.setExpectedMethod("POST");
+ identityResolverEndpoint.setNeedsAuth(true);
+ identityResolverEndpoint.setResponse(200,
String.format("{\"user_name\":\"%s\"}", "xxx"));
+
+ server.start();
+ server.setEndpoints(Map.of(TEST_IDENTITY_RESOLVER_ENDPOINT_PATH,
identityResolverEndpoint));
+
+ final TrustManager[] trustingTrustManager = new TrustManager[]{new
TrustingTrustManager()};
+
+ final SSLContext sc = SSLContext.getInstance("TLSv1.3");
+ sc.init(null, trustingTrustManager, new
java.security.SecureRandom());
+
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
+ HttpsURLConnection.setDefaultHostnameVerifier(new
BlindHostnameVerifier());
+ }
+ catch (Exception e)
+ {
+ throw new RuntimeException(e);
+ }
+
+ final Map<String, Object> authProviderAttributes = new HashMap<>();
+ String testOAuthProvider = "testOAuthProvider";
+ authProviderAttributes.put(ConfiguredObject.NAME, testOAuthProvider);
+ authProviderAttributes.put(ConfiguredObject.TYPE, "OAuth2");
+ authProviderAttributes.put("clientId", TEST_CLIENT_ID);
+ authProviderAttributes.put("clientSecret", TEST_CLIENT_SECRET);
+ authProviderAttributes.put("identityResolverType",
TEST_IDENTITY_RESOLVER_TYPE);
+ authProviderAttributes.put("authorizationEndpointURI",
+ String.format(TEST_URI_PATTERN, TEST_ENDPOINT_HOST,
server.getPort(), TEST_AUTHORIZATION_ENDPOINT_PATH));
+ authProviderAttributes.put("tokenEndpointURI",
+ String.format(TEST_URI_PATTERN, TEST_ENDPOINT_HOST,
server.getPort(), TEST_TOKEN_ENDPOINT_PATH));
+ authProviderAttributes.put("tokenEndpointNeedsAuth",
TEST_AUTHORIZATION_ENDPOINT_NEEDS_AUTH);
+ authProviderAttributes.put("identityResolverEndpointURI",
+ String.format(TEST_URI_PATTERN, TEST_ENDPOINT_HOST,
server.getPort(), TEST_IDENTITY_RESOLVER_ENDPOINT_PATH));
+ authProviderAttributes.put("postLogoutURI",
+ String.format(TEST_URI_PATTERN, TEST_ENDPOINT_HOST,
server.getPort(), TEST_POST_LOGOUT_PATH));
+ authProviderAttributes.put("scope", TEST_SCOPE);
+ authProviderAttributes.put("trustStore", TEST_TRUST_STORE_NAME);
+ authProviderAttributes.put("secureOnlyMechanisms", List.of());
+ final AuthenticationProvider<?> auth =
_broker.createChild(AuthenticationProvider.class, authProviderAttributes);
+ final AmqpPort<?> port = (AmqpPort<?>)
_broker.getPorts().stream().filter(p -> "AMQP".equals(p.getName()))
+ .findFirst().orElse(null);
+ if (port != null)
{
- if (queue.getName().equals(queueName))
+ final AuthenticationProvider<?> authProvider =
(AuthenticationProvider<?>) port
+ .getAttribute(Port.AUTHENTICATION_PROVIDER);
+ if (authProvider != null)
{
- return queue;
+ _tempAuthProvider = authProvider.getName();
}
+ port.setAttributes(Map.of(Port.AUTHENTICATION_PROVIDER,
testOAuthProvider));
}
- throw new NotFoundException(String.format("Queue '%s' not found",
queueName));
}
private class PortExtractingLauncherListener implements
SystemLauncherListener
@@ -452,30 +550,25 @@ public class EmbeddedBrokerPerClassAdminImpl implements
BrokerAdmin
@Override
public void beforeStartup()
{
-
+ // logic not used in tests
}
@Override
public void errorOnStartup(final RuntimeException e)
{
-
+ // logic not used in tests
}
@Override
public void afterStartup()
{
-
if (_systemConfig == null)
{
throw new IllegalStateException("System config is required");
}
_broker = (Broker<?>) _systemConfig.getContainer();
- Collection<Port> ports = _broker.getChildren(Port.class);
- for (Port port : ports)
- {
- _ports.put(port.getName(), port.getBoundPort());
- }
+ _broker.getChildren(Port.class).forEach(port ->
_ports.put(port.getName(), port.getBoundPort()));
}
@Override
@@ -487,23 +580,22 @@ public class EmbeddedBrokerPerClassAdminImpl implements
BrokerAdmin
@Override
public void onContainerClose(final SystemConfig<?> systemConfig)
{
-
+ // logic not used in tests
}
@Override
public void onShutdown(final int exitCode)
{
-
+ // logic not used in tests
}
@Override
public void exceptionOnShutdown(final Exception e)
{
-
+ // logic not used in tests
}
}
-
private static class UncaughtExceptionHandler implements
Thread.UncaughtExceptionHandler
{
private final AtomicInteger _count = new AtomicInteger(0);
@@ -541,13 +633,44 @@ public class EmbeddedBrokerPerClassAdminImpl implements
BrokerAdmin
@Override
public void exceptionOnShutdown(final Exception e)
{
- if (e instanceof IllegalStateException
- || e instanceof IllegalStateTransitionException)
+ if (e instanceof IllegalStateException || e instanceof
IllegalStateTransitionException)
{
- System.out.println(
- "IllegalStateException occurred on broker shutdown in
test ");
+ LOGGER.error("IllegalStateException occurred on broker
shutdown in test");
}
}
}
+ // sonar: hostname verifier is used for test purposes
+ @SuppressWarnings("java:S4830")
+ private static final class TrustingTrustManager implements X509TrustManager
+ {
+ @Override
+ public void checkClientTrusted(final X509Certificate[] certs, final
String authType)
+ {
+ // trust manager is used for test purposes, always trusts client
+ }
+
+ @Override
+ public void checkServerTrusted(final X509Certificate[] certs, final
String authType)
+ {
+ // trust manager is used for test purposes, always trusts server
+ }
+
+ @Override
+ public X509Certificate[] getAcceptedIssuers()
+ {
+ return new X509Certificate[0];
+ }
+ }
+
+ // sonar: hostname verifier is used for test purposes
+ @SuppressWarnings("java:S5527")
+ private static final class BlindHostnameVerifier implements
HostnameVerifier
+ {
+ @Override
+ public boolean verify(final String arg0, final SSLSession arg1)
+ {
+ return true;
+ }
+ }
}
diff --git
a/systests/systests-utils/src/main/java/org/apache/qpid/tests/utils/OAuth2MockEndpoint.java
b/systests/systests-utils/src/main/java/org/apache/qpid/tests/utils/OAuth2MockEndpoint.java
new file mode 100644
index 0000000000..766d579a94
--- /dev/null
+++
b/systests/systests-utils/src/main/java/org/apache/qpid/tests/utils/OAuth2MockEndpoint.java
@@ -0,0 +1,134 @@
+/*
+ *
+ * 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.qpid.tests.utils;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.Base64;
+import java.util.HashMap;
+import java.util.Map;
+
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+
+@SuppressWarnings({"java:S116"})
+// sonar complains about variable names
+class OAuth2MockEndpoint
+{
+ private final Map<String, String> _expectedParameters = new HashMap<>();
+
+ private HttpServletResponse _servletResponse;
+ private String _expectedMethod;
+ private String _responseString;
+ private int _responseCode = 200;
+ private boolean _needsAuth;
+
+ public void handleRequest(final HttpServletRequest request, final
HttpServletResponse response) throws IOException
+ {
+ _servletResponse = response;
+ response.setContentType("application/json");
+ if (_needsAuth)
+ {
+ final String expected = "Basic " +
+ Base64.getEncoder().encodeToString(("testClientId" + ":" +
+ "testClientSecret").getBytes(StandardCharsets.UTF_8));
+ doAssertEquals("Authorization required", expected,
request.getHeader("Authorization"));
+ }
+ if (_expectedMethod != null)
+ {
+ doAssertEquals("Request uses unexpected HTTP method",
_expectedMethod, request.getMethod());
+ }
+ final Map<String, String[]> parameters = request.getParameterMap();
+ for (final Map.Entry<String, String> entry :
_expectedParameters.entrySet())
+ {
+ final String expectedParameter = entry.getKey();
+ final String[] parameterValues = parameters.get(expectedParameter);
+ doAssertTrue(String.format("Request is missing parameter '%s'",
expectedParameter),
+ parameters.containsKey(expectedParameter));
+ doAssertEquals(String.format("Request has parameter '%s' specified
more than once", expectedParameter),
+ 1, parameterValues.length);
+ doAssertEquals(String.format("Request parameter '%s' has
unexpected value", expectedParameter),
+ _expectedParameters.get(expectedParameter),
parameterValues[0]);
+ }
+
+ if (_responseCode != 0)
+ {
+ response.setStatus(_responseCode);
+ }
+
response.getOutputStream().write(_responseString.getBytes(StandardCharsets.UTF_8));
+ }
+
+ public void putExpectedParameter(final String key, final String value)
+ {
+ _expectedParameters.put(key, value);
+ }
+
+ public void setExpectedMethod(final String expectedMethod)
+ {
+ _expectedMethod = expectedMethod;
+ }
+
+ public void setResponseString(final String responseString)
+ {
+ _responseString = responseString;
+ }
+
+ public void setResponseCode(final int responseCode)
+ {
+ _responseCode = responseCode;
+ }
+
+ public void setResponse(final int code, final String message)
+ {
+ setResponseCode(code);
+ setResponseString(message);
+ }
+
+ public void setNeedsAuth(final boolean needsAuth)
+ {
+ this._needsAuth = needsAuth;
+ }
+
+ private void doAssertEquals(final String msg, final Object expected, final
Object actual) throws IOException
+ {
+ if ((expected == null && actual != null) || (expected != null &&
!expected.equals(actual)))
+ {
+ sendError(String.format("%s; Expected: '%s'; Actual: '%s'", msg,
expected, actual));
+ }
+ }
+
+ private void doAssertTrue(final String msg, final boolean condition)
throws IOException
+ {
+ if (!condition)
+ {
+ sendError(msg);
+ }
+ }
+
+ private void sendError(final String errorDescription) throws IOException
+ {
+ _servletResponse.setStatus(500);
+ final String responseString =
String.format("{\"error\":\"test_failure\",\"error_description\":\"%s\"}",
+ errorDescription);
+ _servletResponse.getOutputStream().write(responseString.getBytes());
+ throw new AssertionError(responseString);
+ }
+}
diff --git
a/systests/systests-utils/src/main/java/org/apache/qpid/tests/utils/OAuth2MockEndpointHolder.java
b/systests/systests-utils/src/main/java/org/apache/qpid/tests/utils/OAuth2MockEndpointHolder.java
new file mode 100644
index 0000000000..dd54318588
--- /dev/null
+++
b/systests/systests-utils/src/main/java/org/apache/qpid/tests/utils/OAuth2MockEndpointHolder.java
@@ -0,0 +1,180 @@
+/*
+ *
+ * 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.qpid.tests.utils;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+
+import javax.net.ssl.SSLEngine;
+
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import org.eclipse.jetty.server.HttpConfiguration;
+import org.eclipse.jetty.server.HttpConnectionFactory;
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.server.SecureRequestCustomizer;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.ServerConnector;
+import org.eclipse.jetty.server.handler.AbstractHandler;
+import org.eclipse.jetty.util.resource.Resource;
+import org.eclipse.jetty.util.ssl.SslContextFactory;
+
+import org.apache.qpid.server.configuration.CommonProperties;
+import org.apache.qpid.server.transport.network.security.ssl.SSLUtil;
+
+@SuppressWarnings({"java:S116"})
+// sonar complains about variable names
+public class OAuth2MockEndpointHolder
+{
+ private final Server _server;
+ private final ServerConnector _connector;
+ private volatile Map<String, OAuth2MockEndpoint> _endpoints;
+
+ OAuth2MockEndpointHolder(final String keyStorePath, final String
keyStorePassword, final String keyStoreType)
+ throws IOException
+ {
+ this(Map.of(), keyStorePath, keyStorePassword, keyStoreType);
+ }
+
+ private OAuth2MockEndpointHolder(final Map<String, OAuth2MockEndpoint>
endpoints,
+ final String keyStorePath,
+ final String keyStorePassword,
+ final String keyStoreType)
+ throws IOException
+ {
+ _endpoints = endpoints;
+ final List<String> protocolAllowList =
+
getSystemPropertyAsList(CommonProperties.QPID_SECURITY_TLS_PROTOCOL_ALLOW_LIST,
+
CommonProperties.QPID_SECURITY_TLS_PROTOCOL_ALLOW_LIST_DEFAULT);
+ final List<String> protocolDenyList =
+
getSystemPropertyAsList(CommonProperties.QPID_SECURITY_TLS_PROTOCOL_DENY_LIST,
+
CommonProperties.QPID_SECURITY_TLS_PROTOCOL_DENY_LIST_DEFAULT);
+ final List<String> cipherSuiteAllowList =
+
getSystemPropertyAsList(CommonProperties.QPID_SECURITY_TLS_CIPHER_SUITE_ALLOW_LIST,
+
CommonProperties.QPID_SECURITY_TLS_CIPHER_SUITE_ALLOW_LIST_DEFAULT);
+ final List<String> cipherSuiteDenyList =
+
getSystemPropertyAsList(CommonProperties.QPID_SECURITY_TLS_CIPHER_SUITE_DENY_LIST,
+
CommonProperties.QPID_SECURITY_TLS_CIPHER_SUITE_DENY_LIST_DEFAULT);
+
+ _server = new Server();
+ final SslContextFactory.Server sslContextFactory = new
SslContextFactory.Server()
+ {
+ @Override
+ public void customize(final SSLEngine sslEngine)
+ {
+ super.customize(sslEngine);
+ SSLUtil.updateEnabledCipherSuites(sslEngine,
cipherSuiteAllowList, cipherSuiteDenyList);
+ SSLUtil.updateEnabledTlsProtocols(sslEngine,
protocolAllowList, protocolDenyList);
+ }
+ };
+ sslContextFactory.setKeyStorePassword(keyStorePassword);
+
sslContextFactory.setKeyStoreResource(Resource.newResource(keyStorePath));
+ sslContextFactory.setKeyStoreType(keyStoreType);
+
+ // override default jetty excludes as valid IBM JDK are excluded
+ // causing SSL handshake failure (due to default exclude '^SSL_.*$')
+ sslContextFactory.setExcludeCipherSuites("^.*_(MD5|SHA|SHA1)$",
+ "^TLS_RSA_.*$",
+ "^SSL_RSA_.*$",
+ "^.*_NULL_.*$",
+ "^.*_anon_.*$");
+
+ final SecureRequestCustomizer secureRequestCustomizer = new
SecureRequestCustomizer(false);
+ final HttpConfiguration httpsConfig = new HttpConfiguration();
+ httpsConfig.addCustomizer(secureRequestCustomizer);
+
+ _connector = new ServerConnector(_server, sslContextFactory, new
HttpConnectionFactory(httpsConfig));
+ _connector.setPort(0);
+ _connector.setReuseAddress(true);
+ _server.setHandler(new AbstractHandler()
+ {
+ @Override
+ public void handle(final String target,
+ final Request baseRequest,
+ final HttpServletRequest request,
+ final HttpServletResponse response) throws
IOException
+ {
+ baseRequest.setHandled(true);
+
+ try
+ {
+ final OAuth2MockEndpoint
+ mockEndpoint =
_endpoints.get(request.getPathInfo());
+ assertNotNull(mockEndpoint, String.format("Could not find
mock endpoint for request path '%s'",
+
request.getPathInfo()));
+ mockEndpoint.handleRequest(request, response);
+ }
+ catch (Throwable t)
+ {
+ response.setStatus(500);
+
response.getOutputStream().write(String.format("{\"error\":\"test
failure\",\"error_description\":\"%s\"}", t)
+ .getBytes(UTF_8));
+ }
+ }
+ });
+ _server.addConnector(_connector);
+ }
+
+ public void start() throws Exception
+ {
+ _server.start();
+ }
+
+ public void stop() throws Exception
+ {
+ _server.stop();
+ }
+
+ public int getPort()
+ {
+ return _connector.getLocalPort();
+ }
+
+ public void setEndpoints(final Map<String, OAuth2MockEndpoint> endpoints)
+ {
+ _endpoints = endpoints;
+ }
+
+ private List<String> getSystemPropertyAsList(final String propertyName,
final String defaultValue)
+ {
+ final String listAsString = System.getProperty(propertyName,
defaultValue);
+ List<String> listOfStrings = List.of();
+ if (listAsString != null && !"".equals(listAsString))
+ {
+ try
+ {
+ listOfStrings = new
ObjectMapper().readValue(listAsString.getBytes(UTF_8), new TypeReference<>() {
});
+ }
+ catch (IOException e)
+ {
+ listOfStrings = Arrays.asList(listAsString.split("\\s*,\\s*"));
+ }
+ }
+ return listOfStrings;
+ }
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]