diff --git a/pom.xml b/pom.xml index 58d9e6ba..2f67e235 100644 --- a/pom.xml +++ b/pom.xml @@ -5,12 +5,12 @@ com.solace.spring.boot solace-spring-boot-bom - 2.0.0 + 2.1.0 com.solace.spring.cloud solace-spring-cloud-build - 4.2.1-SNAPSHOT + 4.3.0-SNAPSHOT pom Solace Spring Cloud Build @@ -21,20 +21,20 @@ SolaceProducts - 2023.0.2 + 2023.0.3 - 2.0.0 + 2.1.0 - 3.3.1 + 3.3.2 - 5.2.1-SNAPSHOT + 5.3.0-SNAPSHOT - 1.0.2 + 1.1.2 false true diff --git a/solace-spring-cloud-bom/README.md b/solace-spring-cloud-bom/README.md index 6cf1d239..80dd102e 100644 --- a/solace-spring-cloud-bom/README.md +++ b/solace-spring-cloud-bom/README.md @@ -28,6 +28,7 @@ Consult the table below to determine which version of the BOM you need to use: | 2022.0.4 | 3.1.0, 3.2.0 | 3.1.x | | 2023.0.1 | 4.0.0, 4.1.0 | 3.2.x | | 2023.0.2 | 4.2.0 | 3.3.x | +| 2023.0.3 | 4.3.0 | 3.3.x | ## Including the BOM @@ -40,7 +41,7 @@ In addition to showing how to include the BOM, the following snippets also shows com.solace.spring.cloud solace-spring-cloud-bom - 4.2.0 + 4.3.0 pom import @@ -68,7 +69,7 @@ apply plugin: 'io.spring.dependency-management' dependencyManagement { imports { - mavenBom "com.solace.spring.cloud:solace-spring-cloud-bom:4.2.0" + mavenBom "com.solace.spring.cloud:solace-spring-cloud-bom:4.3.0" } } @@ -80,7 +81,7 @@ dependencies { ### Using it with Gradle 5 ```groovy dependencies { - implementation(platform("com.solace.spring.cloud:solace-spring-cloud-bom:4.2.0")) + implementation(platform("com.solace.spring.cloud:solace-spring-cloud-bom:4.3.0")) implementation("com.solace.spring.cloud:spring-cloud-starter-stream-solace") } ``` diff --git a/solace-spring-cloud-bom/pom.xml b/solace-spring-cloud-bom/pom.xml index 6ae493fa..349d34c4 100644 --- a/solace-spring-cloud-bom/pom.xml +++ b/solace-spring-cloud-bom/pom.xml @@ -5,13 +5,13 @@ com.solace.spring.cloud solace-spring-cloud-build - 4.2.1-SNAPSHOT + 4.3.0-SNAPSHOT ../pom.xml solace-spring-cloud-bom pom - 4.2.1-SNAPSHOT + 4.3.0-SNAPSHOT Solace Spring Cloud BOM BOM for Solace Spring Cloud diff --git a/solace-spring-cloud-parent/pom.xml b/solace-spring-cloud-parent/pom.xml index ba8260d7..5f3d7c57 100644 --- a/solace-spring-cloud-parent/pom.xml +++ b/solace-spring-cloud-parent/pom.xml @@ -5,7 +5,7 @@ com.solace.spring.cloud solace-spring-cloud-build - 4.2.1-SNAPSHOT + 4.3.0-SNAPSHOT ../pom.xml diff --git a/solace-spring-cloud-starters/solace-spring-cloud-stream-starter/README.adoc b/solace-spring-cloud-starters/solace-spring-cloud-stream-starter/README.adoc index 99a16d77..1babd286 100644 --- a/solace-spring-cloud-starters/solace-spring-cloud-stream-starter/README.adoc +++ b/solace-spring-cloud-starters/solace-spring-cloud-stream-starter/README.adoc @@ -1,5 +1,5 @@ = Spring Cloud Stream Binder for Solace PubSub+ -:revnumber: 5.2.0 +:revnumber: 5.3.0 :toc: preamble :toclevels: 3 :icons: font @@ -175,6 +175,11 @@ Additional session properties not available under the usual `solace.java` prefix See https://github.com/SolaceProducts/solace-spring-boot/tree/master/solace-spring-boot-starters/solace-java-spring-boot-starter#updating-your-application-properties[JCSMP Spring Boot Auto-Configuration documentation] for more info about `solace.java.apiProperties`. ==== +[TIP] +==== +The Solace session can be configured to use OAuth2 authentication. See https://github.com/SolaceDev/solace-spring-boot/tree/master/solace-spring-boot-starters/solace-java-spring-boot-starter#using-oauth2-authentication-scheme-with-solace-java-api[JCSMP Spring Boot: Using OAuth2 Authentication Scheme] for more info. +==== + ==== Solace Consumer Properties The following properties are available for Solace consumers only and must be prefixed with `spring.cloud.stream.solace.bindings.<bindingName>.consumer.` where `bindingName` looks something like `functionName-in-0` as defined in https://docs.spring.io/spring-cloud-stream/docs/{scst-version}/reference/html/spring-cloud-stream.html#_functional_binding_names[Functional Binding Names]. diff --git a/solace-spring-cloud-starters/solace-spring-cloud-stream-starter/pom.xml b/solace-spring-cloud-starters/solace-spring-cloud-stream-starter/pom.xml index 19086828..f0777abc 100644 --- a/solace-spring-cloud-starters/solace-spring-cloud-stream-starter/pom.xml +++ b/solace-spring-cloud-starters/solace-spring-cloud-stream-starter/pom.xml @@ -5,12 +5,12 @@ com.solace.spring.cloud solace-spring-cloud-parent - 4.2.1-SNAPSHOT + 4.3.0-SNAPSHOT ../../solace-spring-cloud-parent/pom.xml spring-cloud-starter-stream-solace - 5.2.1-SNAPSHOT + 5.3.0-SNAPSHOT jar diff --git a/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder-core/pom.xml b/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder-core/pom.xml index 1219b38c..88602e52 100644 --- a/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder-core/pom.xml +++ b/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder-core/pom.xml @@ -5,12 +5,12 @@ com.solace.spring.cloud solace-spring-cloud-parent - 4.2.1-SNAPSHOT + 4.3.0-SNAPSHOT ../../solace-spring-cloud-parent/pom.xml spring-cloud-stream-binder-solace-core - 5.2.1-SNAPSHOT + 5.3.0-SNAPSHOT jar Solace Spring Cloud Stream Binder Core @@ -82,6 +82,12 @@ org.springframework.cloud spring-cloud-stream-test-binder test + + + com.vaadin.external.google + android-json + + diff --git a/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder-core/src/main/java/com/solace/spring/cloud/stream/binder/health/handlers/SolaceSessionEventHandler.java b/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder-core/src/main/java/com/solace/spring/cloud/stream/binder/health/handlers/SolaceSessionEventHandler.java index f95b6a12..ac9091c8 100644 --- a/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder-core/src/main/java/com/solace/spring/cloud/stream/binder/health/handlers/SolaceSessionEventHandler.java +++ b/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder-core/src/main/java/com/solace/spring/cloud/stream/binder/health/handlers/SolaceSessionEventHandler.java @@ -1,16 +1,22 @@ package com.solace.spring.cloud.stream.binder.health.handlers; import com.solace.spring.cloud.stream.binder.health.indicators.SessionHealthIndicator; +import com.solacesystems.jcsmp.DefaultSolaceOAuth2SessionEventHandler; +import com.solacesystems.jcsmp.JCSMPProperties; import com.solacesystems.jcsmp.SessionEventArgs; -import com.solacesystems.jcsmp.SessionEventHandler; +import com.solacesystems.jcsmp.SolaceSessionOAuth2TokenProvider; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.springframework.lang.Nullable; -public class SolaceSessionEventHandler implements SessionEventHandler { +public class SolaceSessionEventHandler extends DefaultSolaceOAuth2SessionEventHandler { private final SessionHealthIndicator sessionHealthIndicator; private static final Log logger = LogFactory.getLog(SolaceSessionEventHandler.class); - public SolaceSessionEventHandler(SessionHealthIndicator sessionHealthIndicator) { + public SolaceSessionEventHandler(JCSMPProperties jcsmpProperties, + @Nullable SolaceSessionOAuth2TokenProvider solaceSessionOAuth2TokenProvider, + SessionHealthIndicator sessionHealthIndicator) { + super(jcsmpProperties, solaceSessionOAuth2TokenProvider); this.sessionHealthIndicator = sessionHealthIndicator; } @@ -19,6 +25,7 @@ public void handleEvent(SessionEventArgs eventArgs) { if (logger.isDebugEnabled()) { logger.debug(String.format("Received Solace JCSMP Session event [%s]", eventArgs)); } + super.handleEvent(eventArgs); switch (eventArgs.getEvent()) { case RECONNECTED -> this.sessionHealthIndicator.up(); case DOWN_ERROR -> this.sessionHealthIndicator.down(eventArgs); diff --git a/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder-core/src/test/java/com/solace/spring/cloud/stream/binder/inbound/acknowledge/JCSMPAcknowledgementCallbackFactoryIT.java b/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder-core/src/test/java/com/solace/spring/cloud/stream/binder/inbound/acknowledge/JCSMPAcknowledgementCallbackFactoryIT.java index fc1ccdbf..0cb88601 100644 --- a/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder-core/src/test/java/com/solace/spring/cloud/stream/binder/inbound/acknowledge/JCSMPAcknowledgementCallbackFactoryIT.java +++ b/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder-core/src/test/java/com/solace/spring/cloud/stream/binder/inbound/acknowledge/JCSMPAcknowledgementCallbackFactoryIT.java @@ -193,7 +193,7 @@ public void testRejectFail(@Values(ints = {1, 255}) int numMessages, logger.info(String.format("Disabling egress for queue %s", queue.getName())); sempV2Api.config().updateMsgVpnQueue((String) jcsmpSession.getProperty(JCSMPProperties.VPN_NAME), - queue.getName(), new ConfigMsgVpnQueue().egressEnabled(false), null); + queue.getName(), new ConfigMsgVpnQueue().egressEnabled(false), null, null); retryAssert(() -> assertFalse(sempV2Api.monitor() .getMsgVpnQueue(vpnName, queue.getName(), null) .getData() @@ -214,7 +214,7 @@ public void testRejectFail(@Values(ints = {1, 255}) int numMessages, logger.info(String.format("Enabling egress for queue %s", queue.getName())); sempV2Api.config().updateMsgVpnQueue((String) jcsmpSession.getProperty(JCSMPProperties.VPN_NAME), - queue.getName(), new ConfigMsgVpnQueue().egressEnabled(true), null); + queue.getName(), new ConfigMsgVpnQueue().egressEnabled(true), null, null); retryAssert(() -> assertTrue(sempV2Api.monitor() .getMsgVpnQueue(vpnName, queue.getName(), null) .getData() @@ -277,7 +277,7 @@ public void testRejectWithErrorQueueFail(@Values(ints = {1, 255}) int numMessage logger.info(String.format("Disabling ingress for error queue %s", errorQueueInfrastructure.getErrorQueueName())); sempV2Api.config().updateMsgVpnQueue((String) jcsmpSession.getProperty(JCSMPProperties.VPN_NAME), - errorQueueInfrastructure.getErrorQueueName(), new ConfigMsgVpnQueue().ingressEnabled(false), null); + errorQueueInfrastructure.getErrorQueueName(), new ConfigMsgVpnQueue().ingressEnabled(false), null, null); retryAssert(() -> assertFalse(sempV2Api.monitor() .getMsgVpnQueue(vpnName, errorQueueInfrastructure.getErrorQueueName(), null) .getData() @@ -349,7 +349,7 @@ public void testRequeueFail(@Values(ints = {1, 255}) int numMessages, logger.info(String.format("Disabling egress for queue %s", queue.getName())); sempV2Api.config().updateMsgVpnQueue((String) jcsmpSession.getProperty(JCSMPProperties.VPN_NAME), - queue.getName(), new ConfigMsgVpnQueue().egressEnabled(false), null); + queue.getName(), new ConfigMsgVpnQueue().egressEnabled(false), null, null); retryAssert(() -> assertFalse(sempV2Api.monitor() .getMsgVpnQueue(vpnName, queue.getName(), null) .getData() @@ -370,7 +370,7 @@ public void testRequeueFail(@Values(ints = {1, 255}) int numMessages, logger.info(String.format("Enabling egress for queue %s", queue.getName())); sempV2Api.config().updateMsgVpnQueue((String) jcsmpSession.getProperty(JCSMPProperties.VPN_NAME), - queue.getName(), new ConfigMsgVpnQueue().egressEnabled(true), null); + queue.getName(), new ConfigMsgVpnQueue().egressEnabled(true), null, null); retryAssert(() -> assertTrue(sempV2Api.monitor() .getMsgVpnQueue(vpnName, queue.getName(), null) .getData() diff --git a/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder-core/src/test/java/com/solace/spring/cloud/stream/binder/util/ErrorQueueRepublishCorrelationKeyIT.java b/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder-core/src/test/java/com/solace/spring/cloud/stream/binder/util/ErrorQueueRepublishCorrelationKeyIT.java index f2f0c1b1..d5c7d76e 100644 --- a/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder-core/src/test/java/com/solace/spring/cloud/stream/binder/util/ErrorQueueRepublishCorrelationKeyIT.java +++ b/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder-core/src/test/java/com/solace/spring/cloud/stream/binder/util/ErrorQueueRepublishCorrelationKeyIT.java @@ -172,7 +172,7 @@ public void testHandleErrorAsyncRetry(boolean isDurable, JCSMPSession jcsmpSessi logger.info(String.format("Shutting down ingress for queue %s", errorQueue.getName())); sempV2Api.config().updateMsgVpnQueue(vpnName, errorQueue.getName(), - new ConfigMsgVpnQueue().ingressEnabled(false), null); + new ConfigMsgVpnQueue().ingressEnabled(false), null, null); retryAssert(() -> assertFalse(sempV2Api.monitor() .getMsgVpnQueue(vpnName, errorQueue.getName(), null) .getData() @@ -183,7 +183,7 @@ public void testHandleErrorAsyncRetry(boolean isDurable, JCSMPSession jcsmpSessi if (key.getErrorQueueDeliveryAttempt() == errorQueueInfrastructure.getMaxDeliveryAttempts()) { logger.info(String.format("Starting ingress for queue %s", errorQueue.getName())); sempV2Api.config().updateMsgVpnQueue(vpnName, errorQueue.getName(), - new ConfigMsgVpnQueue().ingressEnabled(true), null); + new ConfigMsgVpnQueue().ingressEnabled(true), null, null); retryAssert(() -> assertTrue(sempV2Api.monitor() .getMsgVpnQueue(vpnName, errorQueue.getName(), null) .getData() diff --git a/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder-core/src/test/java/com/solace/spring/cloud/stream/binder/util/FlowReceiverContainerIT.java b/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder-core/src/test/java/com/solace/spring/cloud/stream/binder/util/FlowReceiverContainerIT.java index 21ad5ae1..acb85770 100644 --- a/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder-core/src/test/java/com/solace/spring/cloud/stream/binder/util/FlowReceiverContainerIT.java +++ b/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder-core/src/test/java/com/solace/spring/cloud/stream/binder/util/FlowReceiverContainerIT.java @@ -773,7 +773,7 @@ public void testReceiveInterruptedByFlowReconnect(@Values(booleans = {false, tru logger.info(String.format("Disabling egress to queue %s", queue.getName())); sempV2Api.config().updateMsgVpnQueue(vpnName, queue.getName(), new ConfigMsgVpnQueue().egressEnabled(false), - null); + null, null); retryAssert(() -> assertFalse(sempV2Api.monitor() .getMsgVpnQueue(vpnName, queue.getName(), null) .getData() @@ -784,7 +784,7 @@ public void testReceiveInterruptedByFlowReconnect(@Values(booleans = {false, tru logger.info(String.format("Enabling egress to queue %s", queue.getName())); sempV2Api.config().updateMsgVpnQueue(vpnName, queue.getName(), new ConfigMsgVpnQueue().egressEnabled(true), - null); + null, null); retryAssert(() -> assertTrue(sempV2Api.monitor() .getMsgVpnQueue(vpnName, queue.getName(), null) .getData() @@ -871,7 +871,7 @@ public void testAcknowledgeAfterFlowReconnect(JCSMPSession jcsmpSession, Queue q logger.info(String.format("Disabling egress to queue %s", queue.getName())); sempV2Api.config().updateMsgVpnQueue(vpnName, queue.getName(), new ConfigMsgVpnQueue().egressEnabled(false), - null); + null, null); retryAssert(() -> assertFalse(sempV2Api.monitor() .getMsgVpnQueue(vpnName, queue.getName(), null) .getData() @@ -881,7 +881,7 @@ public void testAcknowledgeAfterFlowReconnect(JCSMPSession jcsmpSession, Queue q logger.info(String.format("Enabling egress to queue %s", queue.getName())); sempV2Api.config().updateMsgVpnQueue(vpnName, queue.getName(), new ConfigMsgVpnQueue().egressEnabled(true), - null); + null, null); retryAssert(() -> assertTrue(sempV2Api.monitor() .getMsgVpnQueue(vpnName, queue.getName(), null) .getData() diff --git a/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/pom.xml b/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/pom.xml index 60a8c1a5..cbb2d929 100644 --- a/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/pom.xml +++ b/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/pom.xml @@ -5,12 +5,12 @@ com.solace.spring.cloud solace-spring-cloud-parent - 4.2.1-SNAPSHOT + 4.3.0-SNAPSHOT ../../solace-spring-cloud-parent/pom.xml spring-cloud-stream-binder-solace - 5.2.1-SNAPSHOT + 5.3.0-SNAPSHOT jar Solace Spring Cloud Stream Binder @@ -106,12 +106,23 @@ org.springframework.boot spring-boot-starter-test test + + + com.vaadin.external.google + android-json + + org.springframework.boot spring-boot-starter-web test + + org.springframework.boot + spring-boot-starter-oauth2-client + test + diff --git a/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/main/java/com/solace/spring/cloud/stream/binder/config/SolaceHealthIndicatorsConfiguration.java b/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/main/java/com/solace/spring/cloud/stream/binder/config/SolaceHealthIndicatorsConfiguration.java index 948ed4f3..dbd4d68a 100644 --- a/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/main/java/com/solace/spring/cloud/stream/binder/config/SolaceHealthIndicatorsConfiguration.java +++ b/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/main/java/com/solace/spring/cloud/stream/binder/config/SolaceHealthIndicatorsConfiguration.java @@ -6,6 +6,9 @@ import com.solace.spring.cloud.stream.binder.health.handlers.SolaceSessionEventHandler; import com.solace.spring.cloud.stream.binder.health.indicators.SessionHealthIndicator; import com.solace.spring.cloud.stream.binder.properties.SolaceSessionHealthProperties; +import com.solacesystems.jcsmp.JCSMPProperties; +import com.solacesystems.jcsmp.SolaceSessionOAuth2TokenProvider; +import jakarta.annotation.Nullable; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.boot.actuate.autoconfigure.health.ConditionalOnEnabledHealthIndicator; @@ -40,11 +43,14 @@ public SolaceBinderHealthContributor solaceBinderHealthContributor( } @Bean - public SolaceSessionEventHandler solaceSessionEventHandler(SolaceBinderHealthContributor healthContributor) { + public SolaceSessionEventHandler solaceSessionEventHandler( + JCSMPProperties jcsmpProperties, + @Nullable SolaceSessionOAuth2TokenProvider solaceSessionOAuth2TokenProvider, + SolaceBinderHealthContributor healthContributor) { if (logger.isDebugEnabled()) { logger.debug("Creating Solace Session Event Handler for monitoring Health"); } - return new SolaceSessionEventHandler(healthContributor.getSolaceSessionHealthIndicator()); + return new SolaceSessionEventHandler(jcsmpProperties, solaceSessionOAuth2TokenProvider, healthContributor.getSolaceSessionHealthIndicator()); } } diff --git a/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/main/java/com/solace/spring/cloud/stream/binder/config/SolaceMessageChannelBinderConfiguration.java b/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/main/java/com/solace/spring/cloud/stream/binder/config/SolaceMessageChannelBinderConfiguration.java index c0d31be1..0c056c9a 100644 --- a/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/main/java/com/solace/spring/cloud/stream/binder/config/SolaceMessageChannelBinderConfiguration.java +++ b/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/main/java/com/solace/spring/cloud/stream/binder/config/SolaceMessageChannelBinderConfiguration.java @@ -9,13 +9,15 @@ import com.solacesystems.jcsmp.Context; import com.solacesystems.jcsmp.ContextProperties; import com.solacesystems.jcsmp.JCSMPException; -import com.solacesystems.jcsmp.JCSMPFactory; import com.solacesystems.jcsmp.JCSMPProperties; import com.solacesystems.jcsmp.JCSMPSession; +import com.solacesystems.jcsmp.SolaceSessionOAuth2TokenProvider; +import com.solacesystems.jcsmp.SpringJCSMPFactory; import com.solacesystems.jcsmp.impl.JCSMPBasicSession; import jakarta.annotation.PostConstruct; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.springframework.boot.autoconfigure.security.oauth2.client.servlet.OAuth2ClientAutoConfiguration; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -29,7 +31,7 @@ import static com.solacesystems.jcsmp.XMLMessage.Outcome.REJECTED; @Configuration -@Import(SolaceHealthIndicatorsConfiguration.class) +@Import({SolaceHealthIndicatorsConfiguration.class, OAuth2ClientAutoConfiguration.class}) @EnableConfigurationProperties({SolaceExtendedBindingProperties.class}) public class SolaceMessageChannelBinderConfiguration { private final JCSMPProperties jcsmpProperties; @@ -39,29 +41,37 @@ public class SolaceMessageChannelBinderConfiguration { private JCSMPSession jcsmpSession; private Context context; + @Nullable + private SolaceSessionOAuth2TokenProvider solaceSessionOAuth2TokenProvider; + private static final Log logger = LogFactory.getLog(SolaceMessageChannelBinderConfiguration.class); public SolaceMessageChannelBinderConfiguration(JCSMPProperties jcsmpProperties, SolaceExtendedBindingProperties solaceExtendedBindingProperties, - @Nullable SolaceSessionEventHandler eventHandler) { + @Nullable SolaceSessionEventHandler eventHandler, + @Nullable SolaceSessionOAuth2TokenProvider solaceSessionOAuth2TokenProvider) { this.jcsmpProperties = jcsmpProperties; this.solaceExtendedBindingProperties = solaceExtendedBindingProperties; this.solaceSessionEventHandler = eventHandler; + this.solaceSessionOAuth2TokenProvider = solaceSessionOAuth2TokenProvider; } @PostConstruct private void initSession() throws JCSMPException { - JCSMPProperties jcsmpProperties = (JCSMPProperties) this.jcsmpProperties.clone(); - jcsmpProperties.setProperty(JCSMPProperties.CLIENT_INFO_PROVIDER, new SolaceBinderClientInfoProvider()); + JCSMPProperties solaceJcsmpProperties = (JCSMPProperties) this.jcsmpProperties.clone(); + solaceJcsmpProperties.setProperty(JCSMPProperties.CLIENT_INFO_PROVIDER, new SolaceBinderClientInfoProvider()); try { if (solaceSessionEventHandler != null) { if (logger.isDebugEnabled()) { logger.debug("Registering Solace Session Event handler on session"); } - context = JCSMPFactory.onlyInstance().createContext(new ContextProperties()); - jcsmpSession = JCSMPFactory.onlyInstance().createSession(jcsmpProperties, context, solaceSessionEventHandler); + + SpringJCSMPFactory springJCSMPFactory = new SpringJCSMPFactory(solaceJcsmpProperties, solaceSessionOAuth2TokenProvider); + context = springJCSMPFactory.createContext(new ContextProperties()); + jcsmpSession = springJCSMPFactory.createSession(context, solaceSessionEventHandler); } else { - jcsmpSession = JCSMPFactory.onlyInstance().createSession(jcsmpProperties); + SpringJCSMPFactory springJCSMPFactory = new SpringJCSMPFactory(solaceJcsmpProperties, solaceSessionOAuth2TokenProvider); + jcsmpSession = springJCSMPFactory.createSession(); } logger.info(String.format("Connecting JCSMP session %s", jcsmpSession.getSessionName())); jcsmpSession.connect(); diff --git a/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/main/resources/META-INF/shared.beans b/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/main/resources/META-INF/shared.beans index a13d8ca4..875899cd 100644 --- a/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/main/resources/META-INF/shared.beans +++ b/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/main/resources/META-INF/shared.beans @@ -1,2 +1,5 @@ com.solace.spring.cloud.stream.binder.meter.SolaceMessageMeterBinder -com.solace.spring.cloud.stream.binder.meter.SolaceMeterAccessor \ No newline at end of file +com.solace.spring.cloud.stream.binder.meter.SolaceMeterAccessor +org.springframework.security.oauth2.client.registration.ClientRegistrationRepository +org.springframework.security.oauth2.client.OAuth2AuthorizedClientService +org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository \ No newline at end of file diff --git a/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/java/com/solace/it/util/semp/SempClientException.java b/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/java/com/solace/it/util/semp/SempClientException.java new file mode 100644 index 00000000..3a475da0 --- /dev/null +++ b/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/java/com/solace/it/util/semp/SempClientException.java @@ -0,0 +1,135 @@ +package com.solace.it.util.semp; + +import org.osgi.annotation.versioning.ProviderType; + +public class SempClientException extends RuntimeException { + + public SempClientException(String message) { + super(message); + } + + public SempClientException(String message, Throwable cause) { + super(message, cause); + } + + public SempClientException(Throwable cause) { + super(cause); + } + + + /** + * A class for raising errors when client authentication fails. + * + * @since 1.0 + */ + @ProviderType + public static class AuthenticationException extends SempClientException { + + private static final long serialVersionUID = -4840876322728337412L; + + /** + * Creates an instance of {@code AuthenticationException} when client authentication fails with + * an additional message. + * + * @param message the detailed message. The detailed message is saved for later retrieval by the + * {@link #getMessage()} method. + * @since 1.0 + */ + public AuthenticationException(String message) { + super(message); + } + + /** + * Creates an instance of {@code AuthenticationException} when client authentication fails with + * an additional message and a {@code Throwable}. + * + * @param message the detailed message. The detailed message is saved for later retrieval by the + * {@link #getMessage()} method. + * @param t the cause that is saved for later retrieval by the {@link #getCause()} method. + * A {@code null} value is permitted, and indicates that the cause is + * non-existent or unknown. + * @since 1.0 + */ + public AuthenticationException(String message, Throwable t) { + super(message, t); + } + } + + /** + * A class for raising errors when client authorizations fails, client authorizations unsupported + * or not enabled for the service + * + * @since 1.0 + */ + @ProviderType + public static class AuthorizationException extends SempClientException { + + private static final long serialVersionUID = -2315053666285971354L; + + /** + * Creates an instance of {@code AuthorizationException} when client authorizations fails with + * an additional message. + * + * @param message the detailed message. The detailed message is saved for later retrieval by the + * {@link #getMessage()} method. + * @since 1.0 + */ + public AuthorizationException(String message) { + super(message); + } + + /** + * Creates an instance of {@code AuthorizationException} when client authorizations fails with + * an additional message and a {@code Throwable}. + * + * @param message the detailed message. The detailed message is saved for later retrieval by the + * {@link #getMessage()} method. + * @param t the cause that is saved for later retrieval by the {@link #getCause()} method. + * A {@code null} value is permitted, and indicates that the cause is + * non-existent or unknown. + * @since 1.0 + */ + public AuthorizationException(String message, Throwable t) { + super(message, t); + } + } + + /** + * A class for raising errors when a remote resource like a queue, vpn is not found on a broker. + * + * @since 1.0 + */ + @ProviderType + public static class MissingResourceException extends SempClientException { + + private static final long serialVersionUID = 3777381415318250678L; + + /** + * Creates an instance of {@code MissingResourceException} with a detailed message of missing a + * resource situation. + * + * @param message the detailed message. The detailed message is saved for later retrieval by the + * {@link #getMessage()} method. + * @since 1.0 + */ + public MissingResourceException(String message) { + super(message); + } + + /** + * Creates an instance of {@code MissingResourceException} with a detailed message of missing a + * * resource situation and a {@code Throwable}. + * + * @param message the detailed message. The detailed message is saved for later retrieval by the + * {@link #getMessage()} method. + * @param t the cause that is saved for later retrieval by the {@link #getCause()} method. + * A {@code null} value is permitted, and indicates that the cause is + * non-existent or unknown. + * @since 1.0 + */ + public MissingResourceException(String message, Throwable t) { + super(message, t); + } + + } +} \ No newline at end of file diff --git a/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/java/com/solace/it/util/semp/config/BrokerConfiguratorBuilder.java b/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/java/com/solace/it/util/semp/config/BrokerConfiguratorBuilder.java new file mode 100644 index 00000000..e8b2969a --- /dev/null +++ b/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/java/com/solace/it/util/semp/config/BrokerConfiguratorBuilder.java @@ -0,0 +1,912 @@ +package com.solace.it.util.semp.config; + +import com.solace.it.util.semp.SempClientException; +import com.solace.it.util.semp.SempClientException.AuthenticationException; +import com.solace.it.util.semp.SempClientException.AuthorizationException; +import com.solace.it.util.semp.SempClientException.MissingResourceException; +import com.solace.test.integration.semp.v2.SempV2Api; +import com.solace.test.integration.semp.v2.config.ApiClient; +import com.solace.test.integration.semp.v2.config.ApiException; +import com.solace.test.integration.semp.v2.config.api.AuthenticationOauthProfileApi; +import com.solace.test.integration.semp.v2.config.api.AuthorizationGroupApi; +import com.solace.test.integration.semp.v2.config.api.CertAuthorityApi; +import com.solace.test.integration.semp.v2.config.api.ClientProfileApi; +import com.solace.test.integration.semp.v2.config.api.ClientUsernameApi; +import com.solace.test.integration.semp.v2.config.api.MsgVpnApi; +import com.solace.test.integration.semp.v2.config.api.QueueApi; +import com.solace.test.integration.semp.v2.config.auth.HttpBasicAuth; +import com.solace.test.integration.semp.v2.config.model.ConfigCertAuthority; +import com.solace.test.integration.semp.v2.config.model.ConfigCertAuthorityResponse; +import com.solace.test.integration.semp.v2.config.model.ConfigMsgVpn; +import com.solace.test.integration.semp.v2.config.model.ConfigMsgVpn.AuthenticationBasicTypeEnum; +import com.solace.test.integration.semp.v2.config.model.ConfigMsgVpnAclProfile; +import com.solace.test.integration.semp.v2.config.model.ConfigMsgVpnAclProfilesResponse; +import com.solace.test.integration.semp.v2.config.model.ConfigMsgVpnAuthenticationOauthProfile; +import com.solace.test.integration.semp.v2.config.model.ConfigMsgVpnAuthenticationOauthProfileResponse; +import com.solace.test.integration.semp.v2.config.model.ConfigMsgVpnAuthorizationGroup; +import com.solace.test.integration.semp.v2.config.model.ConfigMsgVpnAuthorizationGroupResponse; +import com.solace.test.integration.semp.v2.config.model.ConfigMsgVpnClientProfile; +import com.solace.test.integration.semp.v2.config.model.ConfigMsgVpnClientProfileResponse; +import com.solace.test.integration.semp.v2.config.model.ConfigMsgVpnClientUsername; +import com.solace.test.integration.semp.v2.config.model.ConfigMsgVpnClientUsernameResponse; +import com.solace.test.integration.semp.v2.config.model.ConfigMsgVpnQueue; +import com.solace.test.integration.semp.v2.config.model.ConfigMsgVpnQueue.AccessTypeEnum; +import com.solace.test.integration.semp.v2.config.model.ConfigMsgVpnQueue.PermissionEnum; +import com.solace.test.integration.semp.v2.config.model.ConfigMsgVpnQueueResponse; +import com.solace.test.integration.semp.v2.config.model.ConfigMsgVpnQueueSubscription; +import com.solace.test.integration.semp.v2.config.model.ConfigMsgVpnQueueSubscriptionResponse; +import com.solace.test.integration.semp.v2.config.model.ConfigMsgVpnQueuesResponse; +import com.solace.test.integration.semp.v2.config.model.ConfigMsgVpnResponse; +import com.solace.test.integration.semp.v2.config.model.ConfigMsgVpnsResponse; +import com.solace.test.integration.semp.v2.config.model.ConfigSempMetaOnlyResponse; +import java.util.Collection; +import java.util.List; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.HttpStatus; + +/** + * Builder for entity that can perform administrator level configuration tasks on a messaging + * broker + */ +public class BrokerConfiguratorBuilder { + + static final Logger logger = LoggerFactory.getLogger(BrokerConfiguratorBuilder.class); + + private final ApiClient theClient; + + public static BrokerConfiguratorBuilder create(SempV2Api sempV2Api) { + return new BrokerConfiguratorBuilder(sempV2Api); + } + + private BrokerConfiguratorBuilder(SempV2Api sempV2Api) { + this.theClient = sempV2Api.config().getApiClient(); + } + + /** + * Enables http request level logging. + * + * @return + */ + public BrokerConfiguratorBuilder withDebugLog() { + this.theClient.setDebugging(true); + return this; + } + + public BrokerConfiguratorBuilder withBasicAuth(String userName, String password) { + this.theClient.setUsername(userName); + this.theClient.setPassword(password); + return this; + } + + public BrokerConfigurator build() { + return new BrokerConfigurator(this.theClient); + } + + static T wrapAndRethrowException(ApiException e, String operation, ApiClient apiClient) + throws SempClientException { + final String userName = ((HttpBasicAuth) apiClient + .getAuthentication("basicAuth")).getUsername(); + if (HttpStatus.NOT_FOUND.value() == e.getCode()) { + throw new MissingResourceException(e.getMessage()); + } else if (HttpStatus.UNAUTHORIZED.value() == e.getCode()) { + throw new AuthenticationException( + String.format("Invalid credentials provided for user %s to perform %s", userName, + operation), e); + } else if (HttpStatus.FORBIDDEN.value() == e.getCode()) { + throw new AuthorizationException( + String.format("User %s not authorized to perform %s", userName, operation), e); + } else { + throw new SempClientException(String.format("%s failed", operation), e); + } + } + + /** + * Entity that can perform administrator level configuration tasks on a messaging broker + */ + public static class BrokerConfigurator { + + private final ApiClient theClient; + + private BrokerConfigurator(final ApiClient theClient) { + this.theClient = theClient; + } + + public MessageVpns vpns() { + return new MessageVpns(this.theClient); + } + + public CertAuthorities certAuthorities() { + return new CertAuthorities(this.theClient); + } + + public Queues queues() { + return new Queues(this.theClient); + } + } + + public static class CertAuthorities { + + private final CertAuthorityApi certAuthorityApi; + private final ApiClient apiClient; + + private CertAuthorities(ApiClient apiClient) { + this.certAuthorityApi = new CertAuthorityApi(apiClient); + this.apiClient = apiClient; + } + + public void setupCertAuthority(String certAuthorityName, String certContent) { + final ConfigCertAuthority ca = new ConfigCertAuthority(); + ca.certAuthorityName(certAuthorityName).certContent(certContent); + try { + final ConfigCertAuthorityResponse response = this.certAuthorityApi + .createCertAuthority(ca, null, null); + if (HttpStatus.OK.value() == response.getMeta().getResponseCode()) { + return; + } else { + throw new SempClientException( + String.format("CertAuthority %s could not be upploaded, response code: %s", + certAuthorityName, response.getMeta().getResponseCode())); + } + } catch (ApiException e) { + BrokerConfiguratorBuilder + .wrapAndRethrowException(e, "Query VPNs", apiClient); + } + } + } + + public static class MessageVpns { + + private final String VPN_CREATE_FAIL = "Message vpn %s could not be created"; + private final String VPN_UPDATE_FAIL = "Message vpn %s could not be updated"; + private final String VPN_DELETE_FAIL = "Message vpn %s could not be deleted"; + + private final MsgVpnApi vpnApi; + private final ApiClient apiClient; + + private MessageVpns(ApiClient apiClient) { + this.vpnApi = new MsgVpnApi(apiClient); + this.apiClient = apiClient; + } + + /** + * Create Message VPN from a given entity + * + * @param vpn entity with vpn properties to be created + * @return instance of freshly created message vpn + */ + public ConfigMsgVpn createVpn(ConfigMsgVpn vpn) { + try { + final ConfigMsgVpnResponse response = this.vpnApi.createMsgVpn(vpn, null, null); + if (HttpStatus.OK.value() == response.getMeta().getResponseCode()) { + return response.getData(); + } else { + throw new SempClientException(String.format(VPN_CREATE_FAIL, vpn.getMsgVpnName())); + } + } catch (ApiException e) { + return BrokerConfiguratorBuilder.wrapAndRethrowException(e, "Create VPN", this.apiClient); + } + } + + /** + * updates Message VPN from a given entity + * + * @param vpn entity with vpn properties to be updated + * @return instance of freshly updated message vpn + */ + public ConfigMsgVpn updateVpn(ConfigMsgVpn vpn) { + try { + final ConfigMsgVpnResponse response = this.vpnApi + .updateMsgVpn(vpn.getMsgVpnName(), vpn, null, null); + if (HttpStatus.OK.value() == response.getMeta().getResponseCode()) { + return response.getData(); + } else { + throw new SempClientException(String.format(VPN_UPDATE_FAIL, vpn.getMsgVpnName())); + } + } catch (ApiException e) { + return BrokerConfiguratorBuilder.wrapAndRethrowException(e, "Update VPN", this.apiClient); + } + } + + /** + * Create Message VPN with default settings and no oauth + * + * @param msgVpnName name of the vpn to be created + * @return instance of freshly created message vpn + * @throws SempClientException thrown if something goes wrong; original exception is returned + * wrapped + */ + public ConfigMsgVpn createVpn(String msgVpnName) { + return createVpn(msgVpnName, 50L, false); + } + + public ConfigMsgVpn createVpn(String msgVpnName, Long msgSpool, + boolean internalBasicAuthEnabled) { + ConfigMsgVpn vpn = new ConfigMsgVpn().enabled(true) + .msgVpnName(msgVpnName) + .maxMsgSpoolUsage(msgSpool) + .authenticationBasicType(internalBasicAuthEnabled ? AuthenticationBasicTypeEnum.INTERNAL + : AuthenticationBasicTypeEnum.NONE); + return createVpn(vpn); + } + + /** + * Creates basic vpn copy with disabled extended services like MQTT, Rest, AMQP + * + * @param from + * @param to + * @return + */ + public ConfigMsgVpn copyVpn(String from, String to) { + ConfigMsgVpn vpn = queryVpn(from); + vpn.msgVpnName(to) + .serviceAmqpPlainTextEnabled(false) + .serviceAmqpTlsEnabled(false) + .serviceMqttPlainTextEnabled(false) + .serviceMqttTlsEnabled(false) + .serviceMqttWebSocketEnabled(false) + .serviceMqttTlsWebSocketEnabled(false) + .serviceRestIncomingPlainTextEnabled(false) + .serviceRestIncomingTlsEnabled(false); + return createVpn(vpn); + } + + /** + * Deletes message vpn with a given name + * + * @param msgVpnName name of the vpn + */ + public void deleteVpn(String msgVpnName) { + try { + final ConfigSempMetaOnlyResponse response = this.vpnApi.deleteMsgVpn(msgVpnName); + if (HttpStatus.OK.value() != response.getMeta().getResponseCode()) { + throw new SempClientException(String.format(VPN_DELETE_FAIL, msgVpnName)); + } + } catch (ApiException e) { + throw new SempClientException(e); + } + } + + /** + * Disables message vpn + * + * @param msgVpnName name of the vpn to be disabled + */ + public void disableVpn(String msgVpnName) { + final ConfigMsgVpn vpn = queryVpn(msgVpnName); + vpn.enabled(false); + updateVpn(vpn); + } + + /** + * Queries all vpns on a broker + * + * @return collection with all vpns on a broker + * @throws SempClientException thrown if something goes wrong; original exception is returned + * wrapped + */ + public Collection queryAllVpns() throws SempClientException { + try { + final ConfigMsgVpnsResponse response = this.vpnApi + .getMsgVpns(null, null, null, null, null); + final Collection cl = response.getData(); + whenNotFound(cl); + return cl; + } catch (ApiException e) { + return BrokerConfiguratorBuilder + .wrapAndRethrowException(e, "Query VPNs", this.apiClient); + } + } + + public ConfigMsgVpn queryVpn(String msgVpnName) throws SempClientException { + try { + final ConfigMsgVpnResponse response = this.vpnApi + .getMsgVpn(msgVpnName, null, null); + if (HttpStatus.OK.value() == response.getMeta().getResponseCode()) { + final ConfigMsgVpn cl = response.getData(); + return cl; + } else { + throw new SempClientException( + String.format("Message vpn %s could not be found", msgVpnName)); + } + } catch (ApiException e) { + return BrokerConfiguratorBuilder + .wrapAndRethrowException(e, "Query VPNs", this.apiClient); + } + } + + /** + * Enables internal basic auth on for the given vpn + * + * @param msgVpnName name of the vpn + * @throws SempClientException thrown if something goes wrong; original exception is returned + * wrapped + */ + public void enableBasicAuth(String msgVpnName) throws SempClientException { + final ConfigMsgVpn vpn = queryVpn(msgVpnName); + vpn.setAuthenticationBasicType(AuthenticationBasicTypeEnum.INTERNAL); + try { + final ConfigMsgVpnResponse response = this.vpnApi.updateMsgVpn(msgVpnName, vpn, null, null); + if (HttpStatus.OK.value() == response.getMeta().getResponseCode()) { + logger.debug("Basic auth enabled for vpn: {}", msgVpnName); + return; + } else { + throw new SempClientException( + String.format("Message vpn %s could not be updated", msgVpnName)); + } + } catch (ApiException e) { + throw new SempClientException(e); + } + } + + /** + * Disables internal basic auth on for the given vpn + * + * @param msgVpnName name of the vpn + * @throws SempClientException thrown if something goes wrong; original exception is returned + * wrapped + */ + public void disableBasicAuth(String msgVpnName) throws SempClientException { + final ConfigMsgVpn vpn = queryVpn(msgVpnName); + vpn.setAuthenticationBasicType(AuthenticationBasicTypeEnum.NONE); + try { + final ConfigMsgVpnResponse response = this.vpnApi.updateMsgVpn(msgVpnName, vpn, null, null); + if (HttpStatus.OK.value() == response.getMeta().getResponseCode()) { + logger.debug("Basic auth disabled for vpn: {}", msgVpnName); + return; + } else { + throw new SempClientException( + String.format("Message vpn %s could not be updated", msgVpnName)); + } + } catch (ApiException e) { + throw new SempClientException(e); + } + } + + /** + * Enables internal client auth on for the given vpn + * + * @param msgVpnName name of the vpn + * @throws SempClientException thrown if something goes wrong; original exception is returned + * wrapped + */ + public void enableClientCertAuth(String msgVpnName) throws SempClientException { + final ConfigMsgVpn vpn = queryVpn(msgVpnName); + vpn.setAuthenticationClientCertEnabled(true); + vpn.authenticationClientCertValidateDateEnabled(true); + vpn.authenticationClientCertAllowApiProvidedUsernameEnabled(true); + try { + final ConfigMsgVpnResponse response = this.vpnApi.updateMsgVpn(msgVpnName, vpn, null, null); + if (HttpStatus.OK.value() == response.getMeta().getResponseCode()) { + logger.debug("Client certificate auth enabled for vpn: {}", msgVpnName); + return; + } else { + throw new SempClientException( + String.format("Message vpn %s could not be updated", msgVpnName)); + } + } catch (ApiException e) { + throw new SempClientException(e); + } + } + + /** + * Disables internal client auth on for the given vpn + * + * @param msgVpnName name of the vpn + * @throws SempClientException thrown if something goes wrong; original exception is returned + * wrapped + */ + public void disableClientCertAuth(String msgVpnName) throws SempClientException { + final ConfigMsgVpn vpn = queryVpn(msgVpnName); + vpn.setAuthenticationClientCertEnabled(false); + try { + final ConfigMsgVpnResponse response = this.vpnApi.updateMsgVpn(msgVpnName, vpn, null, null); + if (HttpStatus.OK.value() == response.getMeta().getResponseCode()) { + logger.debug("Basic auth disabled for vpn: {}", msgVpnName); + return; + } else { + throw new SempClientException( + String.format("Message vpn %s could not be updated", msgVpnName)); + } + } catch (ApiException e) { + throw new SempClientException(e); + } + } + + public void enableKerberosAuth(String msgVpnName, boolean allowApiProvidedUsername) + throws SempClientException { + final ConfigMsgVpn vpn = queryVpn(msgVpnName); + vpn.authenticationKerberosEnabled(true); + vpn.authenticationKerberosAllowApiProvidedUsernameEnabled(allowApiProvidedUsername); + try { + final ConfigMsgVpnResponse response = this.vpnApi.updateMsgVpn(msgVpnName, vpn, null, null); + if (HttpStatus.OK.value() == response.getMeta().getResponseCode()) { + logger.debug("Kerberos auth enabled for vpn: {}", msgVpnName); + return; + } else { + throw new SempClientException( + String.format("Message vpn %s could not be updated", msgVpnName)); + } + } catch (ApiException e) { + throw new SempClientException(e); + } + } + + public void disableKerberosAuth(String msgVpnName) + throws SempClientException { + final ConfigMsgVpn vpn = queryVpn(msgVpnName); + vpn.authenticationKerberosEnabled(false); + try { + final ConfigMsgVpnResponse response = this.vpnApi.updateMsgVpn(msgVpnName, vpn, null, null); + if (HttpStatus.OK.value() == response.getMeta().getResponseCode()) { + logger.debug("Kerberos auth disabled for vpn: {}", msgVpnName); + return; + } else { + throw new SempClientException( + String.format("Message vpn %s could not be updated", msgVpnName)); + } + } catch (ApiException e) { + throw new SempClientException(e); + } + } + + public Collection queryAclProfile(String msgVpnName) + throws SempClientException { + try { + final ConfigMsgVpnAclProfilesResponse response = this.vpnApi + .getMsgVpnAclProfiles(msgVpnName, null, null, null, null, null); + final Collection cl = response.getData(); + whenNotFound(cl); + return cl; + } catch (ApiException e) { + return BrokerConfiguratorBuilder + .wrapAndRethrowException(e, "Query AlcProfiles", this.apiClient); + } + } + + private void whenNotFound(Collection collection) + throws SempClientException { + if (collection == null || collection.isEmpty()) { + final String userName = ((HttpBasicAuth) vpnApi + .getApiClient() + .getAuthentication("basicAuth")) + .getUsername(); + throw new MissingResourceException("Can't find resource"); + } + } + + public void enableOAuthAuth(String msgVpnName) throws SempClientException { + final ConfigMsgVpn vpn = queryVpn(msgVpnName); + vpn.setAuthenticationOauthEnabled(true); + try { + final ConfigMsgVpnResponse response = this.vpnApi.updateMsgVpn(msgVpnName, vpn, null, null); + if (HttpStatus.OK.value() == response.getMeta().getResponseCode()) { + logger.debug("OAuth authentication enabled for vpn: {}", msgVpnName); + return; + } else { + throw new SempClientException(String.format(VPN_UPDATE_FAIL, msgVpnName)); + } + } catch (ApiException e) { + throw new SempClientException(e); + } + } + + public void disableOAuthAuth(String msgVpnName) throws SempClientException { + final ConfigMsgVpn vpn = queryVpn(msgVpnName); + vpn.setAuthenticationOauthEnabled(false); + try { + final ConfigMsgVpnResponse response = this.vpnApi.updateMsgVpn(msgVpnName, vpn, null, null); + if (HttpStatus.OK.value() == response.getMeta().getResponseCode()) { + logger.debug("OAuth authentication disabled for vpn: {}", msgVpnName); + return; + } else { + throw new SempClientException(String.format(VPN_UPDATE_FAIL, msgVpnName)); + } + } catch (ApiException e) { + throw new SempClientException(e); + } + } + + public ConfigMsgVpnClientUsername createClientUsername(String msgVpnName, + String clientUsername) { + try { + final ClientUsernameApi clientUsernameApi = new ClientUsernameApi(this.apiClient); + final ConfigMsgVpnClientUsername msgVpnClientUsername = new ConfigMsgVpnClientUsername() + .clientUsername(clientUsername) + .clientProfileName("default") + .aclProfileName("default") + .enabled(true); + ConfigMsgVpnClientUsernameResponse response = clientUsernameApi.createMsgVpnClientUsername( + msgVpnName, msgVpnClientUsername, null, null); + if (HttpStatus.OK.value() == response.getMeta().getResponseCode()) { + return response.getData(); + } else { + throw new SempClientException("MsgVpnClientUsername Creation Failed"); + } + } catch (ApiException e) { + throw new SempClientException(e); + } + } + + public void deleteClientUsername(String msgVpnName, String clientUsername) { + try { + final ClientUsernameApi clientUsernameApi = new ClientUsernameApi(this.apiClient); + final ConfigSempMetaOnlyResponse response = clientUsernameApi.deleteMsgVpnClientUsername( + msgVpnName, clientUsername); + if (HttpStatus.OK.value() != response.getMeta().getResponseCode()) { + throw new SempClientException( + String.format("Could not delete Client Username %s for Message VPN %s", + clientUsername, msgVpnName)); + } + } catch (ApiException e) { + throw new SempClientException(e); + } + } + + public ConfigMsgVpnClientUsername enableClientUsername(String msgVpnName, + String clientUsername) { + try { + final ClientUsernameApi clientUsernameApi = new ClientUsernameApi(this.apiClient); + final ConfigMsgVpnClientUsername msgVpnClientUsername = new ConfigMsgVpnClientUsername() + .clientUsername(clientUsername) + .enabled(true); + ConfigMsgVpnClientUsernameResponse response = clientUsernameApi.updateMsgVpnClientUsername( + msgVpnName, clientUsername, msgVpnClientUsername, null, null); + if (HttpStatus.OK.value() == response.getMeta().getResponseCode()) { + return response.getData(); + } else { + throw new SempClientException("MsgVpnClientUsername Creation Failed"); + } + } catch (ApiException e) { + throw new SempClientException(e); + } + } + + public ConfigMsgVpnClientProfile updateClientProfile(String msgVpnName, + String clientProfileName) { + try { + final ClientProfileApi clientProfileApi = new ClientProfileApi(this.apiClient); + ConfigMsgVpnClientProfile clientProfile = new ConfigMsgVpnClientProfile() + .clientProfileName(clientProfileName) + .allowGuaranteedEndpointCreateEnabled(true) + .allowGuaranteedMsgReceiveEnabled(true) + .allowGuaranteedMsgSendEnabled(true); + ConfigMsgVpnClientProfileResponse response = clientProfileApi.updateMsgVpnClientProfile( + msgVpnName, clientProfileName, clientProfile, null, null); + if (HttpStatus.OK.value() == response.getMeta().getResponseCode()) { + return response.getData(); + } else { + throw new SempClientException("MsgVpnClientUsername Creation Failed"); + } + } catch (ApiException e) { + throw new SempClientException(e); + } + } + + public ConfigMsgVpnAuthenticationOauthProfile createOAuthProfile(String msgVpnName, + ConfigMsgVpnAuthenticationOauthProfile profile) { + try { + AuthenticationOauthProfileApi oAuthProfileApi = new AuthenticationOauthProfileApi( + this.apiClient); + ConfigMsgVpnAuthenticationOauthProfileResponse response = oAuthProfileApi.createMsgVpnAuthenticationOauthProfile( + msgVpnName, profile, null, null); + if (HttpStatus.OK.value() == response.getMeta().getResponseCode()) { + return response.getData(); + } else { + throw new SempClientException("AuthenticationOauthProfileApi Creation Failed"); + } + } catch (ApiException e) { + throw new SempClientException(e); + } + } + + public void deleteOAuthProfile(String msgVpnName, String oAuthProfileName) { + try { + AuthenticationOauthProfileApi oAuthProfileApi = new AuthenticationOauthProfileApi( + this.apiClient); + final ConfigSempMetaOnlyResponse response = oAuthProfileApi.deleteMsgVpnAuthenticationOauthProfile( + msgVpnName, oAuthProfileName); + if (HttpStatus.OK.value() != response.getMeta().getResponseCode()) { + throw new SempClientException( + String.format("Could not be delete OAuth Profile %s for Message VPN %s", + oAuthProfileName, msgVpnName)); + } + } catch (ApiException e) { + throw new SempClientException(e); + } + } + + public ConfigMsgVpnAuthorizationGroup createAuthorizationGroup(String msgVpnName, + ConfigMsgVpnAuthorizationGroup request) { + try { + AuthorizationGroupApi authorizationGroupApi = new AuthorizationGroupApi(this.apiClient); + ConfigMsgVpnAuthorizationGroupResponse response = authorizationGroupApi.createMsgVpnAuthorizationGroup( + msgVpnName, request, null, null); + if (HttpStatus.OK.value() == response.getMeta().getResponseCode()) { + return response.getData(); + } else { + throw new SempClientException("AuthenticationOauthProfileApi Creation Failed"); + } + } catch (ApiException e) { + throw new SempClientException(e); + } + } + + public void deleteAuthorizationGroup(String msgVpnName, String authorizationGroupName) { + try { + AuthorizationGroupApi authorizationGroupApi = new AuthorizationGroupApi(this.apiClient); + final ConfigSempMetaOnlyResponse response = authorizationGroupApi.deleteMsgVpnAuthorizationGroup( + msgVpnName, authorizationGroupName); + if (HttpStatus.OK.value() != response.getMeta().getResponseCode()) { + throw new SempClientException( + String.format("Could not be delete Authorization Group %s for Message VPN %s", + authorizationGroupName, msgVpnName)); + } + } catch (ApiException e) { + throw new SempClientException(e); + } + } + } + + /** + * Api to create/update queues and topic subscriptions + * + * @exclude + */ + public static class Queues { + + private final QueueApi queueApi; + private final ApiClient apiClient; + + private Queues(ApiClient apiClient) { + this.queueApi = new QueueApi(apiClient); + this.apiClient = apiClient; + } + + public void createQueue(String msgVpnName, String queueName, boolean exclusive) + throws SempClientException { + createQueue(msgVpnName, queueName, exclusive, PermissionEnum.DELETE); + } + + public void createPartitionedQueue(String msgVpnName, String queueName, int partitionsCount) + throws SempClientException { + createQueue(msgVpnName, queueName, false, PermissionEnum.DELETE, partitionsCount); + } + + public void updatePartitionCount(String msgVpnName, String queueName, int newPartitionsCount) + throws SempClientException { + final ConfigMsgVpnQueue queue = queryQueue(msgVpnName, queueName); + queue.setPartitionCount(newPartitionsCount); + updateQueue(queue); + } + + public void createQueue(String msgVpnName, String queueName, boolean exclusive, + PermissionEnum permission) throws SempClientException { + createQueue(msgVpnName, queueName, exclusive, permission, 0); + } + + public void createQueue(String msgVpnName, String queueName, boolean exclusive, + PermissionEnum permission, int partitionsCount) throws SempClientException { + final ConfigMsgVpnQueue queue = new ConfigMsgVpnQueue(); + queue.setQueueName(queueName); + queue.setEgressEnabled(true); + queue.setIngressEnabled(true); + queue.setPermission(permission); + queue.setMaxBindCount(10000L); + queue.setMaxMsgSpoolUsage(1500L); + + if (exclusive) { + queue.setAccessType(AccessTypeEnum.EXCLUSIVE); + } else { + queue.setAccessType(AccessTypeEnum.NON_EXCLUSIVE); + } + + if (partitionsCount > 0) { + queue.setPartitionCount(partitionsCount); + } + + try { + final ConfigMsgVpnQueueResponse response = this.queueApi + .createMsgVpnQueue(msgVpnName, queue, null, null); + if (HttpStatus.OK.value() == response.getMeta().getResponseCode()) { + logger.debug("Queue {} created in vpn {}", queueName, msgVpnName); + return; + } else { + throw new SempClientException( + String.format("Queue %s could not be created", msgVpnName)); + } + } catch (ApiException e) { + BrokerConfiguratorBuilder + .wrapAndRethrowException(e, "Creation of a queue", this.apiClient); + } + } + + public void updateQueue(ConfigMsgVpnQueue q) throws SempClientException { + try { + final ConfigMsgVpnQueueResponse response = this.queueApi + .updateMsgVpnQueue(q.getMsgVpnName(), q.getQueueName(), q, null, null); + if (HttpStatus.OK.value() == response.getMeta().getResponseCode()) { + logger.debug("Queue {} updated in vpn {}", q.getQueueName(), q.getMsgVpnName()); + return; + } else { + throw new SempClientException( + String.format("Queue %s could not be updated", q.getMsgVpnName())); + } + } catch (ApiException e) { + BrokerConfiguratorBuilder + .wrapAndRethrowException(e, "Update of a queue", this.apiClient); + } + } + + private ConfigMsgVpnQueue queryQueue(String msgVpnName, String queueName) + throws SempClientException { + try { + final ConfigMsgVpnQueueResponse response = this.queueApi.getMsgVpnQueue(msgVpnName, + queueName, null, null); + if (HttpStatus.OK.value() == response.getMeta().getResponseCode()) { + final ConfigMsgVpnQueue vpnQueue = response.getData(); + return vpnQueue; + } else { + throw new SempClientException( + String.format("Queue %s could not be found", msgVpnName)); + } + } catch (ApiException e) { + return BrokerConfiguratorBuilder.wrapAndRethrowException(e, "Creation of a queue", + this.apiClient); + } + } + + public void addSubscriptionToQueue(String msgVpnName, String queueName, + String subscriptionTopic) + throws SempClientException { + final ConfigMsgVpnQueueSubscription subscription = new ConfigMsgVpnQueueSubscription(); + subscription.setMsgVpnName(msgVpnName); + subscription.setQueueName(queueName); + subscription.setSubscriptionTopic(subscriptionTopic); + + try { + final ConfigMsgVpnQueueSubscriptionResponse response = this.queueApi + .createMsgVpnQueueSubscription(msgVpnName, queueName, subscription, null, null); + if (HttpStatus.OK.value() == response.getMeta().getResponseCode()) { + logger.debug("Subscription is {} created for the queue {} in vpn {}", subscriptionTopic, + queueName, msgVpnName); + return; + } else { + throw new SempClientException( + String.format("Subscription %s could not be created", msgVpnName)); + } + } catch (ApiException e) { + BrokerConfiguratorBuilder + .wrapAndRethrowException(e, "Creation of a subscription", this.apiClient); + } + } + + public void disableEgressOnQueue(String msgVpnName, String queueName) { + try { + final ConfigMsgVpnQueuesResponse response = this.queueApi + .getMsgVpnQueues(msgVpnName, 10, null, null, null, null); + if (HttpStatus.OK.value() == response.getMeta().getResponseCode()) { + final List queues = response.getData(); + ConfigMsgVpnQueue q = queues.stream().filter(queue -> { + if (queueName.equals(queue.getQueueName())) { + return true; + } + return false; + }).findFirst().orElseThrow(() -> { + return new SempClientException( + String.format("Can't shutdown the queue %s", msgVpnName)); + }); + + q.setEgressEnabled(false); + this.queueApi.updateMsgVpnQueue(msgVpnName, queueName, q, null, null); + } else { + throw new SempClientException( + String.format("Can't shutdown the queue %s", queueName)); + } + } catch (ApiException e) { + BrokerConfiguratorBuilder + .wrapAndRethrowException(e, "Shutdown of the queue " + queueName, this.apiClient); + } + } + + public void reenableEgressOnQueue(String msgVpnName, String queueName) { + try { + final ConfigMsgVpnQueuesResponse response = this.queueApi + .getMsgVpnQueues(msgVpnName, 10, null, null, null, null); + if (HttpStatus.OK.value() == response.getMeta().getResponseCode()) { + final List queues = response.getData(); + ConfigMsgVpnQueue q = queues.stream().filter(queue -> { + if (queueName.equals(queue.getQueueName())) { + return true; + } + return false; + }).findFirst().orElseThrow(() -> { + return new SempClientException( + String.format("Can't shutdown the queue %s", msgVpnName)); + }); + q.setEgressEnabled(true); + this.queueApi.updateMsgVpnQueue(msgVpnName, queueName, q, null, null); + } else { + throw new SempClientException( + String.format("Can't shutdown the queue %s", queueName)); + } + } catch (ApiException e) { + BrokerConfiguratorBuilder + .wrapAndRethrowException(e, "Shutdown of the queue " + queueName, this.apiClient); + } + } + + public void disableIngressOnQueue(String msgVpnName, String queueName) { + try { + final ConfigMsgVpnQueuesResponse response = this.queueApi + .getMsgVpnQueues(msgVpnName, 10, null, null, null, null); + if (HttpStatus.OK.value() == response.getMeta().getResponseCode()) { + final List queues = response.getData(); + ConfigMsgVpnQueue q = queues.stream().filter(queue -> { + if (queueName.equals(queue.getQueueName())) { + return true; + } + return false; + }).findFirst().orElseThrow(() -> { + return new SempClientException( + String.format("Can't shutdown the queue %s", msgVpnName)); + }); + q.setIngressEnabled(false); + this.queueApi.updateMsgVpnQueue(msgVpnName, queueName, q, null, null); + } else { + throw new SempClientException( + String.format("Can't shutdown the queue %s", queueName)); + } + } catch (ApiException e) { + BrokerConfiguratorBuilder + .wrapAndRethrowException(e, "Shutdown of the queue " + queueName, this.apiClient); + } + } + + public void reenableIngressOnQueue(String msgVpnName, String queueName) { + try { + final ConfigMsgVpnQueuesResponse response = this.queueApi + .getMsgVpnQueues(msgVpnName, 10, null, null, null, null); + if (HttpStatus.OK.value() == response.getMeta().getResponseCode()) { + final List queues = response.getData(); + ConfigMsgVpnQueue q = queues.stream().filter(queue -> { + if (queueName.equals(queue.getQueueName())) { + return true; + } + return false; + }).findFirst().orElseThrow(() -> { + return new SempClientException( + String.format("Can't shutdown the queue %s", msgVpnName)); + }); + q.setIngressEnabled(true); + this.queueApi.updateMsgVpnQueue(msgVpnName, queueName, q, null, null); + } else { + throw new SempClientException( + String.format("Can't shutdown the queue %s", queueName)); + } + } catch (ApiException e) { + BrokerConfiguratorBuilder + .wrapAndRethrowException(e, "Shutdown of the queue " + queueName, this.apiClient); + } + } + + public void deleteQueue(String msgVpnName, String queueName) { + try { + final ConfigSempMetaOnlyResponse response = this.queueApi + .deleteMsgVpnQueue(msgVpnName, queueName); + if (HttpStatus.OK.value() == response.getMeta().getResponseCode()) { + logger.debug("Queue Deleted!"); + } else { + throw new SempClientException( + String.format("Can't shutdown the queue %s", queueName)); + } + } catch (ApiException e) { + if (e.getResponseBody().contains("NOT_FOUND")) { + return; + } + BrokerConfiguratorBuilder + .wrapAndRethrowException(e, "Delete queue failed: " + queueName, this.apiClient); + } + } + } +} \ No newline at end of file diff --git a/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/java/com/solace/it/util/semp/monitor/BrokerMonitorBuilder.java b/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/java/com/solace/it/util/semp/monitor/BrokerMonitorBuilder.java new file mode 100644 index 00000000..71e8dfb7 --- /dev/null +++ b/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/java/com/solace/it/util/semp/monitor/BrokerMonitorBuilder.java @@ -0,0 +1,374 @@ +package com.solace.it.util.semp.monitor; + +import com.solace.it.util.semp.SempClientException; +import com.solace.it.util.semp.SempClientException.AuthenticationException; +import com.solace.it.util.semp.SempClientException.AuthorizationException; +import com.solace.it.util.semp.SempClientException.MissingResourceException; +import com.solace.test.integration.semp.v2.SempV2Api; +import com.solace.test.integration.semp.v2.monitor.ApiClient; +import com.solace.test.integration.semp.v2.monitor.ApiException; +import com.solace.test.integration.semp.v2.monitor.api.ClientProfileApi; +import com.solace.test.integration.semp.v2.monitor.api.MsgVpnApi; +import com.solace.test.integration.semp.v2.monitor.api.QueueApi; +import com.solace.test.integration.semp.v2.monitor.auth.HttpBasicAuth; +import com.solace.test.integration.semp.v2.monitor.model.MonitorMsgVpnClient; +import com.solace.test.integration.semp.v2.monitor.model.MonitorMsgVpnClientConnection; +import com.solace.test.integration.semp.v2.monitor.model.MonitorMsgVpnClientConnectionsResponse; +import com.solace.test.integration.semp.v2.monitor.model.MonitorMsgVpnClientProfile; +import com.solace.test.integration.semp.v2.monitor.model.MonitorMsgVpnClientProfilesResponse; +import com.solace.test.integration.semp.v2.monitor.model.MonitorMsgVpnClientResponse; +import com.solace.test.integration.semp.v2.monitor.model.MonitorMsgVpnClientSubscription; +import com.solace.test.integration.semp.v2.monitor.model.MonitorMsgVpnClientSubscriptionsResponse; +import com.solace.test.integration.semp.v2.monitor.model.MonitorMsgVpnClientsResponse; +import com.solace.test.integration.semp.v2.monitor.model.MonitorMsgVpnQueueSubscription; +import com.solace.test.integration.semp.v2.monitor.model.MonitorMsgVpnQueueSubscriptionsResponse; +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; +import java.util.Optional; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.HttpStatus; + +/** + * Builder for entity that can perform administrator level monitoring tasks on a messaging broker + */ +public class BrokerMonitorBuilder { + + static final Logger logger = LoggerFactory.getLogger(BrokerMonitorBuilder.class); + private final ApiClient theClient; + + public static BrokerMonitorBuilder create(SempV2Api sempV2Api) { + return new BrokerMonitorBuilder(sempV2Api); + } + + private BrokerMonitorBuilder(SempV2Api sempV2Api) { + this.theClient = sempV2Api.monitor().getApiClient(); + } + + public BrokerMonitorBuilder withDebugLog() { + this.theClient.setDebugging(true); + return this; + } + + public BrokerMonitorBuilder withBasicAuth(String userName, String password) { + this.theClient.setUsername(userName); + this.theClient.setPassword(password); + return this; + } + + public BrokerMonitor build() { + return new BrokerMonitor(this.theClient); + } + + /** + * Entity that can perform administrator level monitoring tasks on a messaging broker + */ + public static class BrokerMonitor { + + private final ApiClient theClient; + + private BrokerMonitor(final ApiClient theClient) { + this.theClient = theClient; + } + + public MessageVpnClients vpnClients() { + return new MessageVpnClients(this.theClient); + } + + public QueueClients queueClients() { + return new QueueClients(this.theClient); + } + + public MsgVpnClientProfiles clientProfiles() { + return new MsgVpnClientProfiles(this.theClient); + } + } + + public static class QueueClients { + + private final QueueApi queueApi; + + public QueueClients(ApiClient theClient) { + this.queueApi = new QueueApi(theClient); + } + + public List querySubscriptionsByOriginalClientUsername(String msgVpnName, + String queueName) throws SempClientException { + List subscriptions = new LinkedList<>(); + final MonitorMsgVpnQueueSubscriptionsResponse response; + try { + response = queueApi.getMsgVpnQueueSubscriptions(msgVpnName, queueName, 10, null, + Collections.EMPTY_LIST, Collections.EMPTY_LIST); + final List subscriptionsList = response.getData(); + if (subscriptionsList != null) { + subscriptionsList.forEach(s -> { + if (s != null) { + subscriptions.add(s.getSubscriptionTopic()); + } + }); + } + } catch (ApiException e) { + return wrapAndRethrowException(e, "Query queue subscriptions"); + } + + return subscriptions; + } + + private T wrapAndRethrowException(ApiException e, String operation) + throws SempClientException { + final String userName = ((HttpBasicAuth) queueApi.getApiClient() + .getAuthentication("basicAuth")) + .getUsername(); + if (HttpStatus.NOT_FOUND.value() == e.getCode()) { + throw new MissingResourceException( + String.format("Can't find resource for %s ", userName), e); + } else if (HttpStatus.UNAUTHORIZED.value() == e.getCode()) { + throw new AuthenticationException( + String.format("Invalid credentials provided for user %s to perform %s", userName, + operation), e); + } else if (HttpStatus.FORBIDDEN.value() == e.getCode()) { + throw new AuthorizationException( + String.format("User %s not authorized to perform %s", userName, operation), e); + } else { + throw new SempClientException(String.format("%s failed", operation), e); + } + } + } + + public static class MsgVpnClientProfiles { + + private final ClientProfileApi clientProfileApi; + + private MsgVpnClientProfiles(final ApiClient theClient) { + this.clientProfileApi = new ClientProfileApi(theClient); + } + + public MonitorMsgVpnClientProfile queryClientProfile(String msgVpnName, String clientName) + throws SempClientException { + try { + MonitorMsgVpnClientProfilesResponse response = this.clientProfileApi + .getMsgVpnClientProfiles(msgVpnName, 10, null, Collections.EMPTY_LIST, + Collections.EMPTY_LIST); + if (HttpStatus.OK.value() == response.getMeta().getResponseCode()) { + final List clProfiles = response.getData(); + final Optional cp = clProfiles.stream() + .filter(clientProfile -> { + if (clientProfile != null && clientName.equals( + clientProfile.getClientProfileName())) { + return true; + } else { + return false; + } + }).findFirst(); + + try { + Thread.sleep(6000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + if (cp.isPresent()) { + return cp.get(); + } else { + throw new MissingResourceException("Can't find resource"); + } + } else { + throw new MissingResourceException("Can't find resource"); + } + } catch (ApiException e) { + return wrapAndRethrowException(e, "Query client connections"); + } + } + + private T wrapAndRethrowException(ApiException e, String operation) + throws SempClientException { + final String userName = ((HttpBasicAuth) clientProfileApi.getApiClient() + .getAuthentication("basicAuth")) + .getUsername(); + + if (HttpStatus.NOT_FOUND.value() == e.getCode()) { + throw new MissingResourceException( + String.format("Can't find resource for %s ", userName), e); + } else if (HttpStatus.UNAUTHORIZED.value() == e.getCode()) { + throw new AuthenticationException( + String.format("Invalid credentials provided for user %s to perform %s", userName, + operation), e); + } else if (HttpStatus.FORBIDDEN.value() == e.getCode()) { + throw new AuthorizationException( + String.format("User %s not authorized to perform %s", userName, operation), e); + } else { + throw new SempClientException(String.format("%s failed", operation), e); + } + } + } + + public static class MessageVpnClients { + + private final MsgVpnApi vpnApi; + + private MessageVpnClients(ApiClient apiClient) { + this.vpnApi = new MsgVpnApi(apiClient); + } + + public Collection queryVpnClientConnections(String msgVpnName, + String clientName) throws SempClientException { + try { + final MonitorMsgVpnClientConnectionsResponse response = this.vpnApi + .getMsgVpnClientConnections(msgVpnName, + clientName, 100, null, null, + null); + return response.getData(); + } catch (ApiException e) { + return wrapAndRethrowException(e, "Query client connections"); + } + } + + public List querySubscriptionsByOriginalClientUsername(String msgVpnName, + String originalClientUsername) throws SempClientException { + final List subscriptions = new LinkedList<>(); + try { + final MonitorMsgVpnClientsResponse response = this.vpnApi + .getMsgVpnClients(msgVpnName, 10, null, Collections.EMPTY_LIST, + Collections.EMPTY_LIST); + + final List clients = response.getData(); + + final Optional theClient = clients.stream().filter(client -> { + if (client != null && originalClientUsername + .equals(client.getOriginalClientUsername())) { + return true; + } else { + return false; + } + }).findFirst(); + + theClient.ifPresent(cl -> { + try { + final MonitorMsgVpnClientSubscriptionsResponse r = this.vpnApi + .getMsgVpnClientSubscriptions(msgVpnName, cl.getClientName(), 10, + null, Collections.EMPTY_LIST, Collections.EMPTY_LIST); + final List allSubscriptions = r.getData(); + allSubscriptions.forEach(s -> { + subscriptions.add(s.getSubscriptionTopic()); + }); + } catch (ApiException e) { + logger.warn("can't find any subscription", e); + } + }); + } catch (Exception e) { + logger.warn("can't find any subscription", e); + // + } + return subscriptions; + } + + public MonitorMsgVpnClient queryVpnClientByClientName(String msgVpnName, String clientName) + throws SempClientException { + try { + final MonitorMsgVpnClientResponse response = this.vpnApi + .getMsgVpnClient(msgVpnName, clientName, null); + return response.getData(); + } catch (ApiException e) { + return wrapAndRethrowException(e, "Query vpn client"); + } + } + + public List queryVpnClientsByClientName(String msgVpnName, + String clientName) throws SempClientException { + final Collection all = queryVpnClients(msgVpnName); + List result = new LinkedList<>(); + for (MonitorMsgVpnClient c : all) { + if (c != null && clientName.equals(c.getClientName())) { + result.add(c); + } + } + + if (!result.isEmpty()) { + return result; + } + throw new MissingResourceException( + String.format("Can't find client with client name %s", clientName)); + } + + public List queryVpnClientsByUserName(String msgVpnName, + String clientUsername) + throws SempClientException { + final Collection all = queryVpnClients(msgVpnName); + List result = new LinkedList<>(); + for (MonitorMsgVpnClient c : all) { + if (c != null && clientUsername.equals(c.getClientUsername())) { + result.add(c); + } + } + + if (!result.isEmpty()) { + return result; + } + throw new MissingResourceException( + String.format("Can't find client with user name %s", clientUsername)); + } + + public List queryVpnClientsByClientUser(String msgVpnName, + String clientUser) throws SempClientException { + final Collection all = queryVpnClients(msgVpnName); + List result = new LinkedList<>(); + for (MonitorMsgVpnClient c : all) { + if (c != null && clientUser.equals(c.getUser())) { + result.add(c); + } + } + + if (!result.isEmpty()) { + return result; + } + throw new MissingResourceException( + String.format("Can't find client with user %s", clientUser)); + } + + public Collection queryVpnClients(String msgVpnName) + throws SempClientException { + try { + final MonitorMsgVpnClientsResponse response = + this.vpnApi.getMsgVpnClients(msgVpnName, 10000, null, null, null); + final Collection cl = response.getData(); + whenNotFound(cl); + return cl; + } catch (ApiException e) { + return wrapAndRethrowException(e, "Query vpn client"); + } + } + + private T wrapAndRethrowException(ApiException e, String operation) + throws SempClientException { + // TBD if not found == no vpns or == bad request + final String userName = ((HttpBasicAuth) vpnApi.getApiClient() + .getAuthentication("basicAuth")) + .getUsername(); + + if (HttpStatus.NOT_FOUND.value() == e.getCode()) { + throw new MissingResourceException( + String.format("Can't find resource for %s ", userName), e); + } else if (HttpStatus.UNAUTHORIZED.value() == e.getCode()) { + throw new AuthenticationException( + String.format("Invalid credentials provided for user %s to perform %s", userName, + operation), e); + } else if (HttpStatus.FORBIDDEN.value() == e.getCode()) { + throw new AuthorizationException( + String.format("User %s not authorized to perform %s", userName, operation), e); + } else { + throw new SempClientException(String.format("%s failed", operation), e); + } + } + + private void whenNotFound(Collection collection) throws SempClientException { + if (collection == null || collection.isEmpty()) { + final String userName = ((HttpBasicAuth) vpnApi.getApiClient() + .getAuthentication("basicAuth")) + .getUsername(); + throw new MissingResourceException("Can't find resource"); + } + } + } +} \ No newline at end of file diff --git a/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/java/com/solace/spring/cloud/stream/binder/SolaceBinderBasicIT.java b/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/java/com/solace/spring/cloud/stream/binder/SolaceBinderBasicIT.java index 7d80b1e2..e7673295 100644 --- a/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/java/com/solace/spring/cloud/stream/binder/SolaceBinderBasicIT.java +++ b/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/java/com/solace/spring/cloud/stream/binder/SolaceBinderBasicIT.java @@ -976,7 +976,7 @@ public void testConsumerReconnect( // Minimize message pre-fetch since we're not testing JCSMP, and this influences the test counters sempV2Api.config().updateMsgVpnQueue(vpnName, queue0, new ConfigMsgVpnQueue() - .maxDeliveredUnackedMsgsPerFlow((long) consumerProperties.getExtension().getBatchMaxSize()), null); + .maxDeliveredUnackedMsgsPerFlow((long) consumerProperties.getExtension().getBatchMaxSize()), null, null); retryAssert(() -> assertThat(sempV2Api.monitor() .getMsgVpnQueue(vpnName, queue0, null) .getData() @@ -1023,11 +1023,11 @@ public void testConsumerReconnect( Thread.sleep(TimeUnit.SECONDS.toMillis(5)); logger.info(String.format("Disabling egress to queue %s", queue0)); - sempV2Api.config().updateMsgVpnQueue(vpnName, queue0, new ConfigMsgVpnQueue().egressEnabled(false), null); + sempV2Api.config().updateMsgVpnQueue(vpnName, queue0, new ConfigMsgVpnQueue().egressEnabled(false), null, null); Thread.sleep(TimeUnit.SECONDS.toMillis(5)); logger.info(String.format("Enabling egress to queue %s", queue0)); - sempV2Api.config().updateMsgVpnQueue(vpnName, queue0, new ConfigMsgVpnQueue().egressEnabled(true), null); + sempV2Api.config().updateMsgVpnQueue(vpnName, queue0, new ConfigMsgVpnQueue().egressEnabled(true), null, null); Thread.sleep(TimeUnit.SECONDS.toMillis(5)); logger.info("Stopping producer"); @@ -1103,7 +1103,7 @@ public void testConsumerRebind( // Minimize message pre-fetch since we're not testing JCSMP, and this influences the test counters sempV2Api.config().updateMsgVpnQueue(vpnName, queue0, new ConfigMsgVpnQueue() - .maxDeliveredUnackedMsgsPerFlow((long) consumerProperties.getExtension().getBatchMaxSize()), null); + .maxDeliveredUnackedMsgsPerFlow((long) consumerProperties.getExtension().getBatchMaxSize()), null, null); retryAssert(() -> assertThat(sempV2Api.monitor() .getMsgVpnQueue(vpnName, queue0, null) .getData() diff --git a/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/java/com/solace/spring/cloud/stream/binder/SolaceBinderHealthIT.java b/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/java/com/solace/spring/cloud/stream/binder/SolaceBinderHealthIT.java index b705c380..9dabb65b 100644 --- a/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/java/com/solace/spring/cloud/stream/binder/SolaceBinderHealthIT.java +++ b/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/java/com/solace/spring/cloud/stream/binder/SolaceBinderHealthIT.java @@ -177,7 +177,7 @@ public void testConsumerFlowHealthUnhealthy( switch (healthStatus) { case "DOWN" -> sempV2Api.config().deleteMsgVpnQueue(vpnName, queueName); case "RECONNECTING" -> sempV2Api.config() - .updateMsgVpnQueue(vpnName, queueName, new ConfigMsgVpnQueue().egressEnabled(false), null); + .updateMsgVpnQueue(vpnName, queueName, new ConfigMsgVpnQueue().egressEnabled(false), null, null); default -> throw new IllegalArgumentException("No test for health status: " + healthStatus); } @@ -189,7 +189,7 @@ public void testConsumerFlowHealthUnhealthy( if (healthStatus.equals("RECONNECTING")) { sempV2Api.config() - .updateMsgVpnQueue(vpnName, queueName, new ConfigMsgVpnQueue().egressEnabled(true), null); + .updateMsgVpnQueue(vpnName, queueName, new ConfigMsgVpnQueue().egressEnabled(true), null, null); retryAssert(2, TimeUnit.MINUTES, () -> assertThat(bindingsHealthContributor) .asInstanceOf(InstanceOfAssertFactories.type(BindingsHealthContributor.class)) diff --git a/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/java/com/solace/spring/cloud/stream/binder/SolaceBinderMeterIT.java b/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/java/com/solace/spring/cloud/stream/binder/SolaceBinderMeterIT.java index cf1dd5eb..fe8778b2 100644 --- a/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/java/com/solace/spring/cloud/stream/binder/SolaceBinderMeterIT.java +++ b/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/java/com/solace/spring/cloud/stream/binder/SolaceBinderMeterIT.java @@ -214,7 +214,7 @@ public void testProducerMeters(@Values(booleans = {false, true}) boolean batched (String) jcsmpSession.getProperty(JCSMPProperties.VPN_NAME), queue.getName(), new ConfigMsgVpnQueueSubscription().subscriptionTopic(destination0), - null); + null, null); SolaceTestBinder binder = context.getBinder(); diff --git a/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/java/com/solace/spring/cloud/stream/binder/SolaceBinderProvisioningLifecycleIT.java b/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/java/com/solace/spring/cloud/stream/binder/SolaceBinderProvisioningLifecycleIT.java index 80ce8852..b43c681d 100644 --- a/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/java/com/solace/spring/cloud/stream/binder/SolaceBinderProvisioningLifecycleIT.java +++ b/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/java/com/solace/spring/cloud/stream/binder/SolaceBinderProvisioningLifecycleIT.java @@ -1096,7 +1096,7 @@ public void testFailConsumerWithConcurrencyGreaterThanMax(JCSMPSession jcsmpSess logger.info(String.format("Pre-provisioning queue %s with maxBindCount=%s", queue0, maxBindCount)); jcsmpSession.provision(queue, new EndpointProperties(), JCSMPSession.WAIT_FOR_CONFIRM); sempV2Api.config() - .updateMsgVpnQueue(vpnName, queue0, new ConfigMsgVpnQueue().maxBindCount(maxBindCount), null); + .updateMsgVpnQueue(vpnName, queue0, new ConfigMsgVpnQueue().maxBindCount(maxBindCount), null, null); try { Binding consumerBinding = binder.bindConsumer( @@ -2369,7 +2369,7 @@ void operatorProvisionTopicEndpoint(SempV2Api sempV2Api, String vpnName, String config.setEgressEnabled(true); config.setMsgVpnName(vpnName); // assume TE is created, when not created test will catch it - sempV2Api.config().createMsgVpnTopicEndpoint(vpnName, config, null); + sempV2Api.config().createMsgVpnTopicEndpoint(vpnName, config, null, null); } } \ No newline at end of file diff --git a/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/java/com/solace/spring/cloud/stream/binder/config/SolaceBinderConfigIT.java b/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/java/com/solace/spring/cloud/stream/binder/config/SolaceBinderConfigIT.java index f02f9199..35c1f7cd 100644 --- a/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/java/com/solace/spring/cloud/stream/binder/config/SolaceBinderConfigIT.java +++ b/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/java/com/solace/spring/cloud/stream/binder/config/SolaceBinderConfigIT.java @@ -44,7 +44,7 @@ void setUp(JCSMPProperties jcsmpProperties, ApplicationContext applicationContex jcsmpProperties.setProperty(JCSMPProperties.CLIENT_NAME, clientName); binderConfiguration = new SolaceMessageChannelBinderConfiguration(jcsmpProperties, - new SolaceExtendedBindingProperties(), null); + new SolaceExtendedBindingProperties(), null, null); AutowireCapableBeanFactory beanFactory = applicationContext.getAutowireCapableBeanFactory(); beanFactory.autowireBean(binderConfiguration); binderConfiguration = (SolaceMessageChannelBinderConfiguration) beanFactory.initializeBean(binderConfiguration, diff --git a/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/java/com/solace/spring/cloud/stream/binder/health/handlers/SolaceSessionEventHandlerTest.java b/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/java/com/solace/spring/cloud/stream/binder/health/handlers/SolaceSessionEventHandlerTest.java index a6dc9394..a829d0b0 100644 --- a/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/java/com/solace/spring/cloud/stream/binder/health/handlers/SolaceSessionEventHandlerTest.java +++ b/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/java/com/solace/spring/cloud/stream/binder/health/handlers/SolaceSessionEventHandlerTest.java @@ -1,6 +1,7 @@ package com.solace.spring.cloud.stream.binder.health.handlers; import com.solace.spring.cloud.stream.binder.health.indicators.SessionHealthIndicator; +import com.solacesystems.jcsmp.JCSMPProperties; import com.solacesystems.jcsmp.SessionEvent; import com.solacesystems.jcsmp.SessionEventArgs; import org.junit.jupiter.api.Test; @@ -15,7 +16,7 @@ public class SolaceSessionEventHandlerTest { @Test public void testConnected(@Mock SessionHealthIndicator healthIndicator) { - SolaceSessionEventHandler sessionEventHandler = new SolaceSessionEventHandler(healthIndicator); + SolaceSessionEventHandler sessionEventHandler = new SolaceSessionEventHandler(new JCSMPProperties(), null, healthIndicator); sessionEventHandler.setSessionHealthUp(); Mockito.verify(healthIndicator, Mockito.times(1)).up(); Mockito.verifyNoMoreInteractions(healthIndicator); @@ -28,7 +29,7 @@ public void testHandleEvent(SessionEvent event, @Mock SessionHealthIndicator healthIndicator) { Mockito.when(eventArgs.getEvent()).thenReturn(event); - SolaceSessionEventHandler sessionEventHandler = new SolaceSessionEventHandler(healthIndicator); + SolaceSessionEventHandler sessionEventHandler = new SolaceSessionEventHandler(new JCSMPProperties(), null, healthIndicator); sessionEventHandler.handleEvent(eventArgs); switch (event) { diff --git a/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/java/com/solace/spring/cloud/stream/binder/springBootTests/MultiBinderIT.java b/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/java/com/solace/spring/cloud/stream/binder/springBootTests/multibinder/MultiBinderIT.java similarity index 93% rename from solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/java/com/solace/spring/cloud/stream/binder/springBootTests/MultiBinderIT.java rename to solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/java/com/solace/spring/cloud/stream/binder/springBootTests/multibinder/MultiBinderIT.java index 29ad7329..4e04c141 100644 --- a/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/java/com/solace/spring/cloud/stream/binder/springBootTests/MultiBinderIT.java +++ b/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/java/com/solace/spring/cloud/stream/binder/springBootTests/multibinder/MultiBinderIT.java @@ -1,5 +1,9 @@ -package com.solace.spring.cloud.stream.binder.springBootTests; +package com.solace.spring.cloud.stream.binder.springBootTests.multibinder; +import static org.awaitility.Awaitility.await; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import com.solace.test.integration.testcontainer.PubSubPlusContainer; import com.solacesystems.jcsmp.JCSMPException; import com.solacesystems.jcsmp.JCSMPFactory; @@ -19,10 +23,6 @@ import org.springframework.test.context.DynamicPropertySource; import org.springframework.test.web.servlet.MockMvc; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; - /** * Note: not using the PubSubPlusExtension as it appears to trigger after Spring Boot Application is started. @@ -91,11 +91,14 @@ public void checkSolaceMetricsAreExposed(@Autowired MockMvc mvc) throws Exceptio producer.send(JCSMPFactory.onlyInstance().createBytesXMLMessage(), JCSMPFactory.onlyInstance().createQueue(QUEUE_NAME_PREFIX + QUEUE_NAME_1)); - mvc.perform(get("/actuator/metrics")) - .andExpectAll( + await().until(() -> { + mvc.perform(get("/actuator/metrics")) + .andExpectAll( jsonPath("names", Matchers.hasItem("solace.message.size.payload")), - jsonPath("names", Matchers.hasItem("solace.message.size.total")) - ); + jsonPath("names", Matchers.hasItem("solace.message.size.total"))); + return true; + } + ); } } diff --git a/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/java/com/solace/spring/cloud/stream/binder/springBootTests/SpringCloudStreamApp.java b/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/java/com/solace/spring/cloud/stream/binder/springBootTests/multibinder/SpringCloudStreamApp.java similarity index 52% rename from solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/java/com/solace/spring/cloud/stream/binder/springBootTests/SpringCloudStreamApp.java rename to solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/java/com/solace/spring/cloud/stream/binder/springBootTests/multibinder/SpringCloudStreamApp.java index 673e0773..f9dedf6d 100644 --- a/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/java/com/solace/spring/cloud/stream/binder/springBootTests/SpringCloudStreamApp.java +++ b/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/java/com/solace/spring/cloud/stream/binder/springBootTests/multibinder/SpringCloudStreamApp.java @@ -1,4 +1,4 @@ -package com.solace.spring.cloud.stream.binder.springBootTests; +package com.solace.spring.cloud.stream.binder.springBootTests.multibinder; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @@ -6,6 +6,9 @@ import org.springframework.messaging.Message; import java.util.function.Consumer; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.web.SecurityFilterChain; +import org.springframework.security.web.util.matcher.AntPathRequestMatcher; @SpringBootApplication public class SpringCloudStreamApp { @@ -24,4 +27,12 @@ public Consumer> otherConsume() { return (msg -> System.out.println(msg.getPayload())); } + @Bean + public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { + http.authorizeHttpRequests(requests -> requests + .requestMatchers(new AntPathRequestMatcher("/actuator/*")).permitAll() + .anyRequest().authenticated()); + + return http.build(); + } } diff --git a/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/java/com/solace/spring/cloud/stream/binder/springBootTests/oauth2/MessagingServiceFreeTierBrokerTestContainerWithTlsAndOAuthSetup.java b/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/java/com/solace/spring/cloud/stream/binder/springBootTests/oauth2/MessagingServiceFreeTierBrokerTestContainerWithTlsAndOAuthSetup.java new file mode 100644 index 00000000..82d91931 --- /dev/null +++ b/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/java/com/solace/spring/cloud/stream/binder/springBootTests/oauth2/MessagingServiceFreeTierBrokerTestContainerWithTlsAndOAuthSetup.java @@ -0,0 +1,117 @@ +package com.solace.spring.cloud.stream.binder.springBootTests.oauth2; + +import static org.junit.jupiter.api.Assertions.assertNotNull; +import java.io.File; +import java.time.Duration; +import java.util.function.Consumer; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testcontainers.containers.ComposeContainer; +import org.testcontainers.containers.ContainerState; +import org.testcontainers.containers.wait.strategy.Wait; + +public interface MessagingServiceFreeTierBrokerTestContainerWithTlsAndOAuthSetup { + + String FULL_DOCKER_COMPOSE_FILE_PATH = "src/test/resources/oauth2/free-tier-broker-with-tls-and-oauth-docker-compose.yml"; + String PUBSUB_BROKER_SERVICE_NAME = "solbroker"; + String NGINX_RPROXY_SERVICE_NAME = "solaceoauth"; + String KEYCLOAK_OAUTH_SERVICE_NAME = "keycloak"; + + Logger LOGGER = LoggerFactory.getLogger( + MessagingServiceFreeTierBrokerTestContainerWithTlsAndOAuthSetup.class); + + ComposeContainer COMPOSE_CONTAINER = new ComposeContainer( + new File(FULL_DOCKER_COMPOSE_FILE_PATH)).withLocalCompose(true).withPull(true) + .withExposedService(PUBSUB_BROKER_SERVICE_NAME, 8080) + .withExposedService(PUBSUB_BROKER_SERVICE_NAME, 55443) + .withExposedService(PUBSUB_BROKER_SERVICE_NAME, 55555) + + .withExposedService(NGINX_RPROXY_SERVICE_NAME, 10443) + .withExposedService(NGINX_RPROXY_SERVICE_NAME, 1080) + + .withExposedService(KEYCLOAK_OAUTH_SERVICE_NAME, 8080) + + .waitingFor(PUBSUB_BROKER_SERVICE_NAME, + Wait.forHttp("/").forPort(8080).withStartupTimeout(Duration.ofSeconds(120))) + .waitingFor(NGINX_RPROXY_SERVICE_NAME, + Wait.forHttp("/").forPort(10443).allowInsecure().usingTls() + .withStartupTimeout(Duration.ofSeconds(120))).waitingFor(KEYCLOAK_OAUTH_SERVICE_NAME, + Wait.forHttp("/").forPort(8080).allowInsecure() + .withStartupTimeout(Duration.ofSeconds(120))); + + @BeforeAll + static void startContainer() { + System.setProperty("javax.net.ssl.trustStore", + new File("src/test/resources/oauth2/certs/client/client-truststore.p12").getAbsolutePath()); + System.setProperty("javax.net.ssl.trustStorePassword", "changeMe123"); + System.setProperty("javax.net.ssl.trustStoreType", "PKCS12"); + //System.setProperty("javax.net.debug", "all"); + + COMPOSE_CONTAINER.start(); + } + + @BeforeAll + static void checkContainer() { + String solaceBroker = COMPOSE_CONTAINER.getServiceHost(PUBSUB_BROKER_SERVICE_NAME, 8080); + assertNotNull(solaceBroker, "solace broker host expected to be not null"); + + String nginxProxy = COMPOSE_CONTAINER.getServiceHost(NGINX_RPROXY_SERVICE_NAME, 10443); + assertNotNull(nginxProxy, "nginx proxy host expected to be not null"); + + String keycloak = COMPOSE_CONTAINER.getServiceHost(KEYCLOAK_OAUTH_SERVICE_NAME, 8080); + assertNotNull(keycloak, "keycloak host expected to be not null"); + } + + @AfterAll + static void afterAll() { + final SolaceBroker broker = SolaceBroker.getInstance(); + broker.backupFinalBrokerLogs(); //Backup container logs before it's destroyed + COMPOSE_CONTAINER.stop(); //Destroy the container + } + + class SolaceBroker { + + private static final class LazyHolder { + + static final SolaceBroker INSTANCE = new SolaceBroker(); + } + + + public static SolaceBroker getInstance() { + return LazyHolder.INSTANCE; + } + + private final ComposeContainer container; + + private SolaceBroker(ComposeContainer container) { + this.container = container; + } + + public SolaceBroker() { + this(COMPOSE_CONTAINER); + } + + /** + * bucks up final log form a broker + */ + void backupFinalBrokerLogs() { + final Consumer copyToBrokerJob = containerState -> { + if (containerState.isRunning()) { + try { + containerState.copyFileFromContainer("/usr/sw/jail/logs/debug.log", + "oauth_test_final_debug.log"); + containerState.copyFileFromContainer("/usr/sw/jail/logs/event.log", + "oauth_test_final_event.log"); + } catch (Exception e) { + LOGGER.error("Failed to backup final log from a broker", e); + } + } + }; + // run actual job on a container + container.getContainerByServiceName(PUBSUB_BROKER_SERVICE_NAME + "_1") + .ifPresent(copyToBrokerJob); + } + } +} \ No newline at end of file diff --git a/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/java/com/solace/spring/cloud/stream/binder/springBootTests/oauth2/MultiBinderOAuth2IT.java b/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/java/com/solace/spring/cloud/stream/binder/springBootTests/oauth2/MultiBinderOAuth2IT.java new file mode 100644 index 00000000..acf6b98f --- /dev/null +++ b/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/java/com/solace/spring/cloud/stream/binder/springBootTests/oauth2/MultiBinderOAuth2IT.java @@ -0,0 +1,212 @@ +package com.solace.spring.cloud.stream.binder.springBootTests.oauth2; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.fail; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import com.solace.it.util.semp.config.BrokerConfiguratorBuilder; +import com.solace.it.util.semp.config.BrokerConfiguratorBuilder.BrokerConfigurator; +import com.solace.it.util.semp.monitor.BrokerMonitorBuilder; +import com.solace.it.util.semp.monitor.BrokerMonitorBuilder.BrokerMonitor; +import com.solace.test.integration.semp.v2.SempV2Api; +import com.solace.test.integration.semp.v2.action.ApiException; +import com.solace.test.integration.semp.v2.action.model.ActionMsgVpnClientDisconnect; +import com.solace.test.integration.semp.v2.config.model.ConfigMsgVpnAuthenticationOauthProfile; +import com.solace.test.integration.semp.v2.config.model.ConfigMsgVpnAuthenticationOauthProfile.OauthRoleEnum; +import com.solace.test.integration.semp.v2.monitor.model.MonitorMsgVpnClient; +import java.io.File; +import java.net.URISyntaxException; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import org.assertj.core.util.Files; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.RepeatedTest; +import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.DynamicPropertyRegistry; +import org.springframework.test.context.DynamicPropertySource; +import org.springframework.test.web.servlet.MockMvc; + +@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT) +@AutoConfigureMockMvc +@ActiveProfiles("multibinderOAuth2") +@DirtiesContext //Ensures all listeners are stopped +class MultiBinderOAuth2IT implements + MessagingServiceFreeTierBrokerTestContainerWithTlsAndOAuthSetup { + + private static final Logger logger = LoggerFactory.getLogger(MultiBinderOAuth2IT.class); + private static BrokerConfigurator solaceConfigUtil; + private static BrokerMonitor solaceMonitorUtil; + private static SempV2Api sempV2Api; + + final static String OAUTH_PROFILE_NAME = "SolaceOauthResourceServer"; + final static String MSG_VPN_OAUTH_1 = "OAUTH_1"; + final static String MSG_VPN_OAUTH_2 = "OAUTH_2"; + + final static String REALM_1 = "solace-oauth-resource-server1"; + final static String REALM_2 = "solace-oauth-resource-server2"; + + @DynamicPropertySource + static void registerDynamicProperties(DynamicPropertyRegistry registry) { + String solaceHost = COMPOSE_CONTAINER.getServiceHost(PUBSUB_BROKER_SERVICE_NAME, 55443); + int solaceSecureSMFPort = COMPOSE_CONTAINER.getServicePort(PUBSUB_BROKER_SERVICE_NAME, 55443); + COMPOSE_CONTAINER.getServicePort(PUBSUB_BROKER_SERVICE_NAME, 55443); + registry.add("spring.cloud.stream.binders.solace1.environment.solace.java.host", + () -> String.format("tcps://%s:%s", solaceHost, solaceSecureSMFPort)); + registry.add("spring.cloud.stream.binders.solace2.environment.solace.java.host", + () -> String.format("tcps://%s:%s", solaceHost, solaceSecureSMFPort)); + + String nginxHost = COMPOSE_CONTAINER.getServiceHost(NGINX_RPROXY_SERVICE_NAME, 10443); + int nginxSecurePort = COMPOSE_CONTAINER.getServicePort(NGINX_RPROXY_SERVICE_NAME, 10443); + registry.add("spring.security.oauth2.client.provider.solace1-auth-server.token-uri", + () -> String.format("https://%s:%s/auth/realms/%s/protocol/openid-connect/token", nginxHost, + nginxSecurePort, REALM_1)); + registry.add("spring.security.oauth2.client.provider.solace2-auth-server.token-uri", + () -> String.format("https://%s:%s/auth/realms/%s/protocol/openid-connect/token", nginxHost, + nginxSecurePort, REALM_2)); + } + + @BeforeAll + static void setUp() { + try { + String solaceHost = COMPOSE_CONTAINER.getServiceHost(PUBSUB_BROKER_SERVICE_NAME, 8080); + int solaceSempPort = COMPOSE_CONTAINER.getServicePort(PUBSUB_BROKER_SERVICE_NAME, 8080); + String sempUrl = String.format("http://%s:%s", solaceHost, solaceSempPort); + sempV2Api = new SempV2Api(sempUrl, "admin", "admin"); + solaceConfigUtil = BrokerConfiguratorBuilder.create(sempV2Api).build(); + solaceMonitorUtil = BrokerMonitorBuilder.create(sempV2Api).build(); + + solaceConfigUtil.vpns().copyVpn("default", MSG_VPN_OAUTH_1); + solaceConfigUtil.vpns().updateClientProfile(MSG_VPN_OAUTH_1, "default"); + solaceConfigUtil.vpns().enableClientUsername(MSG_VPN_OAUTH_1, "default"); + + solaceConfigUtil.vpns().copyVpn("default", MSG_VPN_OAUTH_2); + solaceConfigUtil.vpns().updateClientProfile(MSG_VPN_OAUTH_2, "default"); + solaceConfigUtil.vpns().enableClientUsername(MSG_VPN_OAUTH_2, "default"); + + logger.debug("Prepare to upload CA cert to the broker"); + final URL resource = MultiBinderOAuth2IT.class.getClassLoader() + .getResource("oauth2/certs/rootCA/rootCA.pem"); + if (resource != null) { + final File caFile = new File(resource.toURI()); + final String ca = Files.contentOf(caFile, StandardCharsets.US_ASCII); + solaceConfigUtil.certAuthorities().setupCertAuthority("myCA", ca); + logger.debug("CA cert is uploaded to the broker"); + } else { + logger.error("CA cert file can't be uploaded"); + fail("Root certificate file can't be found"); + } + + //Setup Solace PubSub+ for OAuth2 + setupOAuth(MSG_VPN_OAUTH_1, REALM_1); + setupOAuth(MSG_VPN_OAUTH_2, REALM_2); + } catch (URISyntaxException e) { + fail(e); + } + } + + private static void setupOAuth(String msgVpnName, String realmName) { + solaceConfigUtil.vpns().enableOAuthAuth(msgVpnName); + solaceConfigUtil.vpns().createOAuthProfile(msgVpnName, oAuthProfileResourceServer(realmName)); + } + + private static ConfigMsgVpnAuthenticationOauthProfile oAuthProfileResourceServer( + String realmName) { + final String AUTHORIZATION_GROUP_CLAIM_NAME = ""; + final String ENDPOINT_JWKS = + "https://solaceoauth:10443/auth/realms/" + realmName + "/protocol/openid-connect/certs"; + final String ENDPOINT_USERINFO = + "https://solaceoauth:10443/auth/realms/" + realmName + "/protocol/openid-connect/userinfo"; + final String REALM2_ISSUER_IDENTIFIER = "https://solaceoauth:10443/auth/realms/" + realmName; + + return new ConfigMsgVpnAuthenticationOauthProfile().enabled(true) + .oauthProfileName(OAUTH_PROFILE_NAME) + .authorizationGroupsClaimName(AUTHORIZATION_GROUP_CLAIM_NAME) + .issuer(REALM2_ISSUER_IDENTIFIER).endpointJwks(ENDPOINT_JWKS) + .endpointUserinfo(ENDPOINT_USERINFO).resourceServerParseAccessTokenEnabled(true) + .resourceServerRequiredAudience("").resourceServerRequiredIssuer("") + .resourceServerRequiredScope("").resourceServerValidateAudienceEnabled(false) + .resourceServerValidateIssuerEnabled(false).resourceServerValidateScopeEnabled(false) + .resourceServerValidateTypeEnabled(false).oauthRole(OauthRoleEnum.RESOURCE_SERVER); + } + + @Test + void checkHealthOfMultipleSolaceBinders(@Autowired MockMvc mvc) throws Exception { + mvc.perform(get("/actuator/health")) + .andExpectAll(status().isOk(), jsonPath("components.binders.components.solace1").exists(), + jsonPath("components.binders.components.solace1.status").value("UP"), + jsonPath("components.binders.components.solace2").exists(), + jsonPath("components.binders.components.solace2.status").value("UP")); + } + + @Test + void checkHealthOfMultipleSolaceBindersWhenForceReconnect(@Autowired MockMvc mvc) { + try { + CountDownLatch forcedReconnect = new CountDownLatch(1); + int numberOfReconnects = 5; + AtomicBoolean failed = new AtomicBoolean(false); + Thread t = new Thread(() -> { + try { + for (int i = 0; i < numberOfReconnects; i++) { + try { + String msgVPnName = i % 2 == 0 ? MSG_VPN_OAUTH_1 : MSG_VPN_OAUTH_2; + MonitorMsgVpnClient msgVpnClient = solaceMonitorUtil.vpnClients() + .queryVpnClients(msgVPnName) + .stream() + .filter(client -> client.getClientUsername().startsWith("default")) + .findFirst().orElse(null); + + if (msgVpnClient == null) { + throw new RuntimeException("Client not found"); + } + + logger.info("Forcing Session Reconnect for client: {}", msgVpnClient.getClientName()); + sempV2Api.action() + .doMsgVpnClientDisconnect(msgVPnName, msgVpnClient.getClientName(), + new ActionMsgVpnClientDisconnect()); + } catch (ApiException e) { + throw new RuntimeException(e); + } finally { + Thread.sleep(10_000); //Introduced delay between force reconnects + } + } + } catch (Exception e) { + logger.error("Failed to force reconnect", e); + failed.set(true); + } finally { + forcedReconnect.countDown(); + } + }); + t.start(); + + logger.info("Wait for force session reconnect, to refresh token"); + boolean success = forcedReconnect.await(3, TimeUnit.MINUTES); + if (!success) { + fail("Timed out waiting for token refresh"); + } + + assertFalse(failed.get(), "Failed to force reconnect"); + + //Thread.sleep(10000); //Wait for reconnect to complete + + mvc.perform(get("/actuator/health")) + .andExpectAll(status().isOk(), jsonPath("components.binders.components.solace1").exists(), + jsonPath("components.binders.components.solace1.status").value("UP"), + jsonPath("components.binders.components.solace2").exists(), + jsonPath("components.binders.components.solace2.status").value("UP")); + } catch (Exception e) { + fail(e); + } + } +} \ No newline at end of file diff --git a/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/java/com/solace/spring/cloud/stream/binder/springBootTests/oauth2/SpringCloudStreamOAuth2App.java b/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/java/com/solace/spring/cloud/stream/binder/springBootTests/oauth2/SpringCloudStreamOAuth2App.java new file mode 100644 index 00000000..41a21be2 --- /dev/null +++ b/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/java/com/solace/spring/cloud/stream/binder/springBootTests/oauth2/SpringCloudStreamOAuth2App.java @@ -0,0 +1,39 @@ +package com.solace.spring.cloud.stream.binder.springBootTests.oauth2; + +import java.util.function.Consumer; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.Bean; +import org.springframework.messaging.Message; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.web.SecurityFilterChain; +import org.springframework.security.web.util.matcher.AntPathRequestMatcher; + +@SpringBootApplication +@EnableWebSecurity +public class SpringCloudStreamOAuth2App { + + public static void main(String[] args) { + SpringApplication.run(SpringCloudStreamOAuth2App.class, args); + } + + @Bean + public Consumer> consume() { + return (msg -> System.out.println(msg.getPayload())); + } + + @Bean + public Consumer> otherConsume() { + return (msg -> System.out.println(msg.getPayload())); + } + + @Bean + public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { + http.authorizeHttpRequests(requests -> requests + .requestMatchers(new AntPathRequestMatcher("/actuator/*")).permitAll() + .anyRequest().authenticated()); + + return http.build(); + } +} \ No newline at end of file diff --git a/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/resources/application-multibinderOAuth2.yml b/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/resources/application-multibinderOAuth2.yml new file mode 100644 index 00000000..650f23fa --- /dev/null +++ b/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/resources/application-multibinderOAuth2.yml @@ -0,0 +1,89 @@ +spring: + security: + oauth2: + client: + registration: + solace1-oauth2-client: + provider: solace1-auth-server + client-id: solclient_oauth + client-secret: j6gWnw13iqzJfFZzlqzaQabQgXza4oHl + authorization-grant-type: client_credentials + scope: openid + solace2-oauth2-client: + provider: solace2-auth-server + client-id: solclient_oauth + client-secret: j6gWnw13iqzJfFZzlqzaQabQgXza4oHl + authorization-grant-type: client_credentials + scope: openid + provider: + solace1-auth-server: + token-uri: https://localhost:10443/auth/realms/solace-oauth-resource-server1/protocol/openid-connect/token + solace2-auth-server: + token-uri: https://localhost:10443/auth/realms/solace-oauth-resource-server2/protocol/openid-connect/token + + cloud: + function: + definition: "consume;otherConsume" + stream: + binders: + solace1: + type: solace + environment: + solace: + java: + host: placeholder + msgVpn: OAUTH_1 + connectRetries: 10 + reconnectRetries: 10 + reconnectRetryWaitInMillis: 2000 + oauth2ClientRegistrationId: solace1-oauth2-client + apiProperties: + SSL_VALIDATE_CERTIFICATE: false ## Because using self-signed certificate + AUTHENTICATION_SCHEME: AUTHENTICATION_SCHEME_OAUTH2 + + solace2: + type: solace + environment: + solace: + java: + host: placeholder + msgVpn: OAUTH_2 + connectRetries: 10 + reconnectRetries: 10 + reconnectRetryWaitInMillis: 2000 + oauth2ClientRegistrationId: solace2-oauth2-client + apiProperties: + SSL_VALIDATE_CERTIFICATE: false ## Because using self-signed certificate + AUTHENTICATION_SCHEME: AUTHENTICATION_SCHEME_OAUTH2 + + bindings: + consume-in-0: + destination: MultiBinder/Queue/1 + group: myConsumerGroup + binder: solace1 + otherConsume-in-0: + destination: MultiBinder/Queue/2 + group: myConsumerGroup + binder: solace2 + default-binder: solace1 + +# solace: +# default: +# consumer: +# add-destination-as-subscription-to-queue: false +# provision-durable-queue: false +# producer: +# provision-durable-queue: false + +management: + endpoint: + health: + show-components: always + show-details: always + endpoints: + web: # Actuator web endpoint configuration. For more info: https://docs.spring.io/spring-boot/docs/current/reference/html/actuator.html#actuator.endpoints + exposure: + include: 'health,metrics' + health: + binders: + enabled: true diff --git a/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/resources/oauth2/certs/README.txt b/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/resources/oauth2/certs/README.txt new file mode 100644 index 00000000..d1e517c7 --- /dev/null +++ b/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/resources/oauth2/certs/README.txt @@ -0,0 +1,96 @@ +Note: where a password is required, the password is "changeMe123" + +Create a Certificate Authority (CA) and use it to sign certificates for a Solace PubSub+ broker, a Solace PubSub+ client, and a Keycloak server. + +The directory contains the following files: +- solbroker_san.conf: Solace PubSub+ broker certificate signing request (CSR) configuration file, mainly for Subject Alternative Name (SAN) extension. +- keycloak_san.conf: Keycloak server certificate signing request (CSR) configuration file, mainly for Subject Alternative Name (SAN) extension. + + +Create sub-directories: +======================= +mkdir rootCA +mkdir broker +mkdir client +mkdir keycloak + +Root CA Key/Certificate: +======================== +1. Generate a 4096-bit RSA private key for the Root CA: + +openssl genrsa -out ./rootCA/rootCA.key 4096 + +2. Create a self-signed Root CA certificate valid for 20 years: +openssl req -x509 -new -nodes -key ./rootCA/rootCA.key -sha256 -days 7300 -out ./rootCA/rootCA.crt \ + -subj "/C=CA/ST=Ontario/L=Kanata/O=Solace Systems/CN=Root CA" + +3. Create a pem file with the key and certificate: +cat ./rootCA/rootCA.key ./rootCA/rootCA.crt > ./rootCA/rootCA.pem + +4. verify the certificate: +openssl x509 -in ./rootCA/rootCA.crt -text -noout + +Solace Broker Key/Certificate: +============================== +1. Generate a 2048-bit RSA private key for the server: +openssl genrsa -out ./broker/solbroker.key 2048 + +2. Create a CSR (Certificate Signing Request) for the server: +openssl req -new -key ./broker/solbroker.key -out ./broker/solbroker.csr -config ./solbroker_san.conf + +3. Sign the server CSR with your Root CA to generate the server certificate valid for 20 years: +openssl x509 -req -in ./broker/solbroker.csr -CA ./rootCA/rootCA.crt -CAkey ./rootCA/rootCA.key -CAcreateserial \ + -out ./broker/solbroker.crt -days 7300 -sha256 -extensions req_ext -extfile ./solbroker_san.conf + +4. Create a pem file with the key and certificate: +cat ./broker/solbroker.key ./broker/solbroker.crt > ./broker/solbroker.pem + +5. verify the certificate: +openssl x509 -in ./broker/solbroker.crt -text -noout + +Solace Client Key/Certificate: +============================== +1. Generate a 2048-bit RSA private key for the client: +openssl genrsa -out ./client/client.key 2048 + +2. Create a CSR (Certificate Signing Request) for the client: +openssl req -new -key ./client/client.key -out ./client/client.csr \ + -subj "/C=CA/ST=Ontario/L=Kanata/O=Solace Systems/CN=solclient" + +3. Sign the client CSR with your Root CA to generate the client certificate valid for 20 years: +openssl x509 -req -in ./client/client.csr -CA ./rootCA/rootCA.crt -CAkey ./rootCA/rootCA.key -CAcreateserial \ + -out ./client/client.crt -days 7300 -sha256 + +4. Create a pem file with the key and certificate: +cat ./client/client.key ./client/client.crt > ./client/client.pem + +5. verify the certificate: +openssl x509 -in ./client/client.crt -text -noout + +6. Create and verify a client truststore containing the Root CA certificate: +openssl x509 -outform der -in ./rootCA/rootCA.pem -out ./rootCA/rootCA.der +keytool -import -trustcacerts -alias root_ca -file ./rootCA/rootCA.der -keystore ./client/client-truststore.p12 -storepass changeMe123 -noprompt +keytool -v -list -keystore ./client/client-truststore.p12 -storepass changeMe123 + +7. Create a client keystore containing the client key and certificate: +openssl pkcs12 -export -in ./client/client.pem -inkey ./client/client.key -name client -out ./client/client.p12 -passout pass:changeMe123 +keytool -importkeystore -srckeystore ./client/client.p12 -srcstoretype PKCS12 -destkeystore ./client/client-keystore.jks -deststoretype JKS -srcstorepass changeMe123 -deststorepass changeMe123 + + +Keycloak Server Key/Certificate: +================================ +1. Generate a 2048-bit RSA private key for the keycloak: +openssl genrsa -out ./keycloak/keycloak.key 2048 + +2. Create a CSR (Certificate Signing Request) for the keycloak: +openssl req -new -key ./keycloak/keycloak.key -out ./keycloak/keycloak.csr -config ./keycloak_san.conf + +3. Sign the keycloak CSR with your Root CA to generate the keycloak certificate valid for 20 years: +openssl x509 -req -in ./keycloak/keycloak.csr -CA ./rootCA/rootCA.crt -CAkey ./rootCA/rootCA.key -CAcreateserial \ + -out ./keycloak/keycloak.crt -days 7300 -sha256 -extensions req_ext -extfile ./keycloak_san.conf + +4. Create a pem file with the key and certificate: +cat ./keycloak/keycloak.key ./keycloak/keycloak.crt > ./keycloak/keycloak.pem + +5. verify the certificate: +openssl x509 -in ./keycloak/keycloak.crt -text -noout diff --git a/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/resources/oauth2/certs/broker/solbroker.crt b/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/resources/oauth2/certs/broker/solbroker.crt new file mode 100644 index 00000000..70b2af51 --- /dev/null +++ b/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/resources/oauth2/certs/broker/solbroker.crt @@ -0,0 +1,28 @@ +-----BEGIN CERTIFICATE----- +MIIEzDCCArSgAwIBAgIUbAVQlzPN4AFLWLI4lE+Rzw1cG+cwDQYJKoZIhvcNAQEL +BQAwWzELMAkGA1UEBhMCQ0ExEDAOBgNVBAgMB09udGFyaW8xDzANBgNVBAcMBkth +bmF0YTEXMBUGA1UECgwOU29sYWNlIFN5c3RlbXMxEDAOBgNVBAMMB1Jvb3QgQ0Ew +HhcNMjQwNzEwMTUzMDQ3WhcNNDQwNzA1MTUzMDQ3WjB2MQswCQYDVQQGEwJDQTEQ +MA4GA1UECAwHT250YXJpbzEPMA0GA1UEBwwGS2FuYXRhMRcwFQYDVQQKDA5Tb2xh +Y2UgU3lzdGVtczEXMBUGA1UECwwOU29sYWNlIFN5c3RlbXMxEjAQBgNVBAMMCXNv +bGJyb2tlcjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJ1ElE74dz7g +eSeGgnH6KjcyE1p6Dc3y/6mlr6ST7IWtFAfbWRdwALDEZWUYXyDkpvEQtxZDrgkF +Ss8sbL1dskAj3xtdU1wh6ibqgH5hpzlrUhgCIssDVgMOppRxZXnjYEN85nW3r2r5 +bbgVST/bJ+YEHX66cb1TK0ISqcugzySd5bQSwW9G0vKMvQv9VWoVRWtVpPm/s5Y3 +2FAd65rTJWBJhPowzsqKMNfx9JYEScFGfq6YOPjgm1SVoAG9nJXaUjt7C9S/fS+i +eIafRYTOhQUIjXw6IgL29CTmoKRajbxjsjpi0C/zPg/1amB4OEceQatNXwc0YZgM +j6dxPTsqKisCAwEAAaNtMGswaQYDVR0RBGIwYIIJc29sYnJva2Vyggpzb2xicm9r +ZXIxggtzb2xicm9rZXJfMYIJbG9jYWxob3N0ggpzb2xicm9rZXIyggtzb2xicm9r +ZXJfMocEfwAAAYcQAAAAAAAAAAAAAAAAAAAAATANBgkqhkiG9w0BAQsFAAOCAgEA +TYl/0ObS+eP9sb/IKLOXXaWS6K+1HQaqwRW0HJ0QBgwjfL9WhWupp1eujd+QFIGq +eESZZvhxcF7lQIoaAQe/gn7wAL4ZIPJMlUdLiTBNacKCNqH9z/Meu+UHMN2mxJmD +JlNVbmuWcpbE82nGylv+iZc8DWofnoyoyBRhJLPVqeyv3YUuIchkUxRjl7XbpQ9U +4nG4+9pACEM4pAZZHG8aXcca8PjwlcdJjfYt0n4700NlCqpREe/w/GJZFjk+Pf87 +O1t9Xycrux0k4Q8eLiO0qNNeJWEaKEdoUGkmO5/mu3ghq2/wRB9Vmmq+z/UaZW+j +l62lIowEO1eRtoYthvNh4eZ0kWlfE5m0F2kor69QsiB0Qgfr7ucMamAzNLD+JEgU ++scfnmW48y0eGqipKt9A02Bo6d2X36oG/IbTAYPuPJiDwWej77mgBfHDQhyV/+YI +ayB/9bur4CW3wov6+a+YO/Q2rhU3ONs4EsQNGL8lo4opG9yCop9PR/BrcFcHvu+b +61I6u1ZLGk2e5KUxKk7XEAkG/L1Y8JEY5D1j2f9HUa0rQ2NO69Ry0qMvMeN0PKQt +XDs0INdK0VGyuD4BxHlwHutKXBVpsZyoxUWsR21UzczxuLqncHGJ7+/MbJBLElwa +/5J9RRDdKubX3mv5tVV8fpBn6bYYYThZsDX7qMU1vgQ= +-----END CERTIFICATE----- diff --git a/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/resources/oauth2/certs/broker/solbroker.csr b/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/resources/oauth2/certs/broker/solbroker.csr new file mode 100644 index 00000000..3969f7cc --- /dev/null +++ b/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/resources/oauth2/certs/broker/solbroker.csr @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE REQUEST----- +MIIDNzCCAh8CAQAwdjELMAkGA1UEBhMCQ0ExEDAOBgNVBAgMB09udGFyaW8xDzAN +BgNVBAcMBkthbmF0YTEXMBUGA1UECgwOU29sYWNlIFN5c3RlbXMxFzAVBgNVBAsM +DlNvbGFjZSBTeXN0ZW1zMRIwEAYDVQQDDAlzb2xicm9rZXIwggEiMA0GCSqGSIb3 +DQEBAQUAA4IBDwAwggEKAoIBAQCdRJRO+Hc+4HknhoJx+io3MhNaeg3N8v+ppa+k +k+yFrRQH21kXcACwxGVlGF8g5KbxELcWQ64JBUrPLGy9XbJAI98bXVNcIeom6oB+ +Yac5a1IYAiLLA1YDDqaUcWV542BDfOZ1t69q+W24FUk/2yfmBB1+unG9UytCEqnL +oM8kneW0EsFvRtLyjL0L/VVqFUVrVaT5v7OWN9hQHeua0yVgSYT6MM7KijDX8fSW +BEnBRn6umDj44JtUlaABvZyV2lI7ewvUv30voniGn0WEzoUFCI18OiIC9vQk5qCk +Wo28Y7I6YtAv8z4P9WpgeDhHHkGrTV8HNGGYDI+ncT07KiorAgMBAAGgfDB6Bgkq +hkiG9w0BCQ4xbTBrMGkGA1UdEQRiMGCCCXNvbGJyb2tlcoIKc29sYnJva2VyMYIL +c29sYnJva2VyXzGCCWxvY2FsaG9zdIIKc29sYnJva2VyMoILc29sYnJva2VyXzKH +BH8AAAGHEAAAAAAAAAAAAAAAAAAAAAEwDQYJKoZIhvcNAQELBQADggEBACYEqj5E +A/4Jl45GrqYmVAc2f+3upB4XY6f67A5RR8q4fogtpU6WDZbxleJhlJceT4DaS4zx +lk5sglzRCPEloskaosZ/fWXJsI/OcAGilcdPkpgPnf24iZCFIfLfhf8Vq4CM8m9M +2pbTyghLPcORkIQ2rATRp/2gHHBWblfXbPw0FCxONyLolMGDMMF6/3z3yjT4pYGd +/wAIgku9dB6og+yZeJ2JozLbml69eXbiASAWVSnkGLNag7u+NXkWiXFRQNQ4jAsi +qYZ0Nl70F5QQ78ko4Nj+96Jo1ET1TON7C2fbl4ueHuHV7pxiGCRxe511OFSFQva6 +ubRf1fqacCMvD7w= +-----END CERTIFICATE REQUEST----- diff --git a/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/resources/oauth2/certs/broker/solbroker.key b/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/resources/oauth2/certs/broker/solbroker.key new file mode 100644 index 00000000..c2632aba --- /dev/null +++ b/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/resources/oauth2/certs/broker/solbroker.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEogIBAAKCAQEAnUSUTvh3PuB5J4aCcfoqNzITWnoNzfL/qaWvpJPsha0UB9tZ +F3AAsMRlZRhfIOSm8RC3FkOuCQVKzyxsvV2yQCPfG11TXCHqJuqAfmGnOWtSGAIi +ywNWAw6mlHFleeNgQ3zmdbevavltuBVJP9sn5gQdfrpxvVMrQhKpy6DPJJ3ltBLB +b0bS8oy9C/1VahVFa1Wk+b+zljfYUB3rmtMlYEmE+jDOyoow1/H0lgRJwUZ+rpg4 ++OCbVJWgAb2cldpSO3sL1L99L6J4hp9FhM6FBQiNfDoiAvb0JOagpFqNvGOyOmLQ +L/M+D/VqYHg4Rx5Bq01fBzRhmAyPp3E9OyoqKwIDAQABAoIBAHsUYuVy+xAQaYEP +eiNtX4CXBiJ3Bzq5BHFmpBGvWxo7HEQR3KXFGCU/bwMxkbGSgTyEkmUwTpHsvGFr +KScCnzAnYsJtxYGDYVdXi3xdPJxpa3Qyp7wuPjBiVOgz3vEHjB0FMO/L89NKph29 +Ovhosc8IRXUawU0kO+SX6p7cmYDTf/EdvCh1M6N1XlsvcOMb82v4arb5XyQ5IKdl +qpm3Q23B1IPiwv4ErzeLHRbZ0XbMdQrSR85oZVAyFdQUjKZs/Bq0rZbrd9yxclBi +k+4PJS3HpDjDwAmIeRVRIlsZdSOLLUOywieDLY8+inCwLXs6A5kRVbbKYWxewehG +47n35EECgYEAzojA0NyA7ihtJHvez/CcbAfSW+/uXEhnEPv++ynnUAmz5unDacnx +yICpFqZCMG8Ob/lrXoFvEMrmyFETBWiF9BlGJY17mtTZkKb83Osz1AGvdeKQQ/zB +5UOLXsQKN+1HAI4VdyI4UutwzIfwcNu6mYqVMFZfBfHZ8iBCR5oR/ZECgYEAwu8o +Tnowfu9uzDMNx8uQRnzCXvkL+jWqqUdfJXiRhsBSgQlfkw62TxByj3wRKIqUjj9E +DzhsXJXsqetzfmVQH12RgVkJ5GkDY060Z8c9GYtx1rPJqtkIu6M+m/yVxZ4Tost6 +K3jvXveJXqu0wlJJMvpzupMBYSpvMo0KBj7rPfsCgYAY484o3YoEKYcNsIfnk12m +f0LQpZeaM3eISnYuGpyvvpuZpm5QX2/t8+NswViUsa2RvQM9fme+JFWvqmWab0BF +bI5RlD1jKWeW0SkEDqxOTm2wzT8JknpjgMJZB1Mb7lJyNK1NkCgthgYv/+nwD+rq ++hKEosQM2VqknVKfgmfMoQKBgDWKGjfzt34lpPjQzOgjMO0rNvd+z5tZQhZcU/Wm +t9Ga4Q4v1OA/GjN9APoHyW6pIUQwfDDx/lEvnGDPGlmM2gTDXkN4gQ8LCLMt2r7m +KhHqCso9dxZFpfBjVb7iEQDF+f6shFGMVbJvqnsmDe+RSimGQGLuHWLilMf9lNNC +VLohAoGAKnfkD7NQgeez9fGrWjFN/8jj7jI3yvVjihpLQBEMMbMLma/V8Btm2/7x +lgWiGMBcfYDqCPbe48xGWiHOKkW3tot5wsQCLZzwTPLYqFgjMDOFfCXmtmlOTMYu ++UdceSxN1JU/wyfXSf7CB1kkwcpOQ1awrccJ75DoHQW6seFjTFk= +-----END RSA PRIVATE KEY----- diff --git a/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/resources/oauth2/certs/broker/solbroker.pem b/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/resources/oauth2/certs/broker/solbroker.pem new file mode 100644 index 00000000..21e12a1b --- /dev/null +++ b/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/resources/oauth2/certs/broker/solbroker.pem @@ -0,0 +1,55 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEogIBAAKCAQEAnUSUTvh3PuB5J4aCcfoqNzITWnoNzfL/qaWvpJPsha0UB9tZ +F3AAsMRlZRhfIOSm8RC3FkOuCQVKzyxsvV2yQCPfG11TXCHqJuqAfmGnOWtSGAIi +ywNWAw6mlHFleeNgQ3zmdbevavltuBVJP9sn5gQdfrpxvVMrQhKpy6DPJJ3ltBLB +b0bS8oy9C/1VahVFa1Wk+b+zljfYUB3rmtMlYEmE+jDOyoow1/H0lgRJwUZ+rpg4 ++OCbVJWgAb2cldpSO3sL1L99L6J4hp9FhM6FBQiNfDoiAvb0JOagpFqNvGOyOmLQ +L/M+D/VqYHg4Rx5Bq01fBzRhmAyPp3E9OyoqKwIDAQABAoIBAHsUYuVy+xAQaYEP +eiNtX4CXBiJ3Bzq5BHFmpBGvWxo7HEQR3KXFGCU/bwMxkbGSgTyEkmUwTpHsvGFr +KScCnzAnYsJtxYGDYVdXi3xdPJxpa3Qyp7wuPjBiVOgz3vEHjB0FMO/L89NKph29 +Ovhosc8IRXUawU0kO+SX6p7cmYDTf/EdvCh1M6N1XlsvcOMb82v4arb5XyQ5IKdl +qpm3Q23B1IPiwv4ErzeLHRbZ0XbMdQrSR85oZVAyFdQUjKZs/Bq0rZbrd9yxclBi +k+4PJS3HpDjDwAmIeRVRIlsZdSOLLUOywieDLY8+inCwLXs6A5kRVbbKYWxewehG +47n35EECgYEAzojA0NyA7ihtJHvez/CcbAfSW+/uXEhnEPv++ynnUAmz5unDacnx +yICpFqZCMG8Ob/lrXoFvEMrmyFETBWiF9BlGJY17mtTZkKb83Osz1AGvdeKQQ/zB +5UOLXsQKN+1HAI4VdyI4UutwzIfwcNu6mYqVMFZfBfHZ8iBCR5oR/ZECgYEAwu8o +Tnowfu9uzDMNx8uQRnzCXvkL+jWqqUdfJXiRhsBSgQlfkw62TxByj3wRKIqUjj9E +DzhsXJXsqetzfmVQH12RgVkJ5GkDY060Z8c9GYtx1rPJqtkIu6M+m/yVxZ4Tost6 +K3jvXveJXqu0wlJJMvpzupMBYSpvMo0KBj7rPfsCgYAY484o3YoEKYcNsIfnk12m +f0LQpZeaM3eISnYuGpyvvpuZpm5QX2/t8+NswViUsa2RvQM9fme+JFWvqmWab0BF +bI5RlD1jKWeW0SkEDqxOTm2wzT8JknpjgMJZB1Mb7lJyNK1NkCgthgYv/+nwD+rq ++hKEosQM2VqknVKfgmfMoQKBgDWKGjfzt34lpPjQzOgjMO0rNvd+z5tZQhZcU/Wm +t9Ga4Q4v1OA/GjN9APoHyW6pIUQwfDDx/lEvnGDPGlmM2gTDXkN4gQ8LCLMt2r7m +KhHqCso9dxZFpfBjVb7iEQDF+f6shFGMVbJvqnsmDe+RSimGQGLuHWLilMf9lNNC +VLohAoGAKnfkD7NQgeez9fGrWjFN/8jj7jI3yvVjihpLQBEMMbMLma/V8Btm2/7x +lgWiGMBcfYDqCPbe48xGWiHOKkW3tot5wsQCLZzwTPLYqFgjMDOFfCXmtmlOTMYu ++UdceSxN1JU/wyfXSf7CB1kkwcpOQ1awrccJ75DoHQW6seFjTFk= +-----END RSA PRIVATE KEY----- +-----BEGIN CERTIFICATE----- +MIIEzDCCArSgAwIBAgIUbAVQlzPN4AFLWLI4lE+Rzw1cG+cwDQYJKoZIhvcNAQEL +BQAwWzELMAkGA1UEBhMCQ0ExEDAOBgNVBAgMB09udGFyaW8xDzANBgNVBAcMBkth +bmF0YTEXMBUGA1UECgwOU29sYWNlIFN5c3RlbXMxEDAOBgNVBAMMB1Jvb3QgQ0Ew +HhcNMjQwNzEwMTUzMDQ3WhcNNDQwNzA1MTUzMDQ3WjB2MQswCQYDVQQGEwJDQTEQ +MA4GA1UECAwHT250YXJpbzEPMA0GA1UEBwwGS2FuYXRhMRcwFQYDVQQKDA5Tb2xh +Y2UgU3lzdGVtczEXMBUGA1UECwwOU29sYWNlIFN5c3RlbXMxEjAQBgNVBAMMCXNv +bGJyb2tlcjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJ1ElE74dz7g +eSeGgnH6KjcyE1p6Dc3y/6mlr6ST7IWtFAfbWRdwALDEZWUYXyDkpvEQtxZDrgkF +Ss8sbL1dskAj3xtdU1wh6ibqgH5hpzlrUhgCIssDVgMOppRxZXnjYEN85nW3r2r5 +bbgVST/bJ+YEHX66cb1TK0ISqcugzySd5bQSwW9G0vKMvQv9VWoVRWtVpPm/s5Y3 +2FAd65rTJWBJhPowzsqKMNfx9JYEScFGfq6YOPjgm1SVoAG9nJXaUjt7C9S/fS+i +eIafRYTOhQUIjXw6IgL29CTmoKRajbxjsjpi0C/zPg/1amB4OEceQatNXwc0YZgM +j6dxPTsqKisCAwEAAaNtMGswaQYDVR0RBGIwYIIJc29sYnJva2Vyggpzb2xicm9r +ZXIxggtzb2xicm9rZXJfMYIJbG9jYWxob3N0ggpzb2xicm9rZXIyggtzb2xicm9r +ZXJfMocEfwAAAYcQAAAAAAAAAAAAAAAAAAAAATANBgkqhkiG9w0BAQsFAAOCAgEA +TYl/0ObS+eP9sb/IKLOXXaWS6K+1HQaqwRW0HJ0QBgwjfL9WhWupp1eujd+QFIGq +eESZZvhxcF7lQIoaAQe/gn7wAL4ZIPJMlUdLiTBNacKCNqH9z/Meu+UHMN2mxJmD +JlNVbmuWcpbE82nGylv+iZc8DWofnoyoyBRhJLPVqeyv3YUuIchkUxRjl7XbpQ9U +4nG4+9pACEM4pAZZHG8aXcca8PjwlcdJjfYt0n4700NlCqpREe/w/GJZFjk+Pf87 +O1t9Xycrux0k4Q8eLiO0qNNeJWEaKEdoUGkmO5/mu3ghq2/wRB9Vmmq+z/UaZW+j +l62lIowEO1eRtoYthvNh4eZ0kWlfE5m0F2kor69QsiB0Qgfr7ucMamAzNLD+JEgU ++scfnmW48y0eGqipKt9A02Bo6d2X36oG/IbTAYPuPJiDwWej77mgBfHDQhyV/+YI +ayB/9bur4CW3wov6+a+YO/Q2rhU3ONs4EsQNGL8lo4opG9yCop9PR/BrcFcHvu+b +61I6u1ZLGk2e5KUxKk7XEAkG/L1Y8JEY5D1j2f9HUa0rQ2NO69Ry0qMvMeN0PKQt +XDs0INdK0VGyuD4BxHlwHutKXBVpsZyoxUWsR21UzczxuLqncHGJ7+/MbJBLElwa +/5J9RRDdKubX3mv5tVV8fpBn6bYYYThZsDX7qMU1vgQ= +-----END CERTIFICATE----- diff --git a/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/resources/oauth2/certs/client/client-keystore.jks b/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/resources/oauth2/certs/client/client-keystore.jks new file mode 100644 index 00000000..68b57de0 Binary files /dev/null and b/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/resources/oauth2/certs/client/client-keystore.jks differ diff --git a/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/resources/oauth2/certs/client/client-truststore.p12 b/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/resources/oauth2/certs/client/client-truststore.p12 new file mode 100644 index 00000000..e915ff1e Binary files /dev/null and b/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/resources/oauth2/certs/client/client-truststore.p12 differ diff --git a/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/resources/oauth2/certs/client/client.crt b/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/resources/oauth2/certs/client/client.crt new file mode 100644 index 00000000..12c61d81 --- /dev/null +++ b/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/resources/oauth2/certs/client/client.crt @@ -0,0 +1,25 @@ +-----BEGIN CERTIFICATE----- +MIIEPzCCAicCFGwFUJczzeABS1iyOJRPkc8NXBvoMA0GCSqGSIb3DQEBCwUAMFsx +CzAJBgNVBAYTAkNBMRAwDgYDVQQIDAdPbnRhcmlvMQ8wDQYDVQQHDAZLYW5hdGEx +FzAVBgNVBAoMDlNvbGFjZSBTeXN0ZW1zMRAwDgYDVQQDDAdSb290IENBMB4XDTI0 +MDcxMDE1MzUyNFoXDTQ0MDcwNTE1MzUyNFowXTELMAkGA1UEBhMCQ0ExEDAOBgNV +BAgMB09udGFyaW8xDzANBgNVBAcMBkthbmF0YTEXMBUGA1UECgwOU29sYWNlIFN5 +c3RlbXMxEjAQBgNVBAMMCXNvbGNsaWVudDCCASIwDQYJKoZIhvcNAQEBBQADggEP +ADCCAQoCggEBAMPxo1xlJD9Yi+fkR9MK7lNHsQ6VLHTad6ktUG+at0rclQfYG2x/ +G1lp812UaceQexOgEPTc7g4+cL8k9zDfJZhjF0hN5jZPrf5r1vP2v9xwdRRebVeZ +juHsjPOjeu+1IbSWOELLUNqqdgt4zxUbEnMjX693n7jivBPWMtqJiVmXLgB/bdA4 +qJQ5DxtTF0jZLpxlVOPKKk/G6HMuW6EevI4XyP2MA1ayUXpTulcj39gCfMN3Bxcp +epIYSV5gzeqV03fuUoh7GrtnBATIQxFe3guJEcoBqT+DUCieH0hRmizA435UK+lp +yzDZWxLjLSh7msnTaIfgKmuNmrJcCk6KulMCAwEAATANBgkqhkiG9w0BAQsFAAOC +AgEAiMRofj8jnppDr+IOCdZTqSUemV/+5+IIYJkMpibPnM/WPQjn95GgxLhfNtAq +gaIOlN/IPEMJCZzCVSX/Fd+5YI10NBX7wBoFVrf0izSp40OAzynqlGrFMPdWQwAJ +9kyqSuZa7R8phZzRAH6IrdsQcrxr7qGc5yBCEXnLTbd5rCEXYh6Yvq5/CspYvuCJ +9kieXujn1XDt/bApFxkKJpOzkXZQxRDBKK8oYxH6u/0uN03M0yXnswiUVBjXEGZE +MpBud1qIOfDOnZeS2yxJRKzX9Q0tXg9CHT0CnQTTC9f31FfDYW/ro81pqnLuzlm7 +RIPb7M6ovgJgFUmvh8lSqz0wDugNfTZZX6QCa/x3kUlCtmxwbLsC4MYmzgy3wtYw +Bt6B8t9KzWCdLRbk+akGOX3nDn8NUpZhqgXyVUY10IxSIShueZ+IIwvdbKEXdIsz +QNyaU76zeD175gPVOb9upuV7R3gJXcbLRQ6gCNoGkZrnTXOjI4wpGFmWHXJOJTDS +nngQ9n1QiGBqYsEun+PRIod90ucwjTYuG88HSqTS8zsYQSf1qvQCG79Piq2osdMW +jxPzdK/bZDNBObSGl/gHkXy9YmZ2MFyQoEaVeRjPUYRcPquALMFYRB9HDBWlYDsb +GTgiYKVIs3FK90XnX7TzXyzH0GyzOCkC/47/dplWkP3WL+8= +-----END CERTIFICATE----- diff --git a/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/resources/oauth2/certs/client/client.csr b/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/resources/oauth2/certs/client/client.csr new file mode 100644 index 00000000..cb3ef9ff --- /dev/null +++ b/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/resources/oauth2/certs/client/client.csr @@ -0,0 +1,17 @@ +-----BEGIN CERTIFICATE REQUEST----- +MIICojCCAYoCAQAwXTELMAkGA1UEBhMCQ0ExEDAOBgNVBAgMB09udGFyaW8xDzAN +BgNVBAcMBkthbmF0YTEXMBUGA1UECgwOU29sYWNlIFN5c3RlbXMxEjAQBgNVBAMM +CXNvbGNsaWVudDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMPxo1xl +JD9Yi+fkR9MK7lNHsQ6VLHTad6ktUG+at0rclQfYG2x/G1lp812UaceQexOgEPTc +7g4+cL8k9zDfJZhjF0hN5jZPrf5r1vP2v9xwdRRebVeZjuHsjPOjeu+1IbSWOELL +UNqqdgt4zxUbEnMjX693n7jivBPWMtqJiVmXLgB/bdA4qJQ5DxtTF0jZLpxlVOPK +Kk/G6HMuW6EevI4XyP2MA1ayUXpTulcj39gCfMN3BxcpepIYSV5gzeqV03fuUoh7 +GrtnBATIQxFe3guJEcoBqT+DUCieH0hRmizA435UK+lpyzDZWxLjLSh7msnTaIfg +KmuNmrJcCk6KulMCAwEAAaAAMA0GCSqGSIb3DQEBCwUAA4IBAQAWOCvnysa8pBPh +H4liajvowUgOBGz8fS87kiy57s9n4IZXYNU/PeNmMrm8RJLseP5UiYRvVaI0Wk8h +2zJmb0g73brW153TPxU9aNDTTcSkMQmS3NMYvt/okxcEWkBoQ6zbgNE0DvCPnCkz +svPASwYrGY0k3gLCWK8+/znrCHuqOAeQKYpdpYiLwJwPc7/iFfWxzdUp2XWV2141 +yRhJPwqlMK5UI8RwpkdkjIljuuaX5zeMtNIHlKtvcxC6B4ZYLii5mpVoOvNS/Wp/ +ZEpoFY6mn70z9uD+gyRyv8Yxwk6IMZnHiJVtcn93+z3O3Ini5hqRTgG9eCiIwFQl +FgEVqEdJ +-----END CERTIFICATE REQUEST----- diff --git a/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/resources/oauth2/certs/client/client.key b/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/resources/oauth2/certs/client/client.key new file mode 100644 index 00000000..6847c05b --- /dev/null +++ b/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/resources/oauth2/certs/client/client.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEAw/GjXGUkP1iL5+RH0wruU0exDpUsdNp3qS1Qb5q3StyVB9gb +bH8bWWnzXZRpx5B7E6AQ9NzuDj5wvyT3MN8lmGMXSE3mNk+t/mvW8/a/3HB1FF5t +V5mO4eyM86N677UhtJY4QstQ2qp2C3jPFRsScyNfr3efuOK8E9Yy2omJWZcuAH9t +0DiolDkPG1MXSNkunGVU48oqT8bocy5boR68jhfI/YwDVrJRelO6VyPf2AJ8w3cH +Fyl6khhJXmDN6pXTd+5SiHsau2cEBMhDEV7eC4kRygGpP4NQKJ4fSFGaLMDjflQr +6WnLMNlbEuMtKHuaydNoh+Aqa42aslwKToq6UwIDAQABAoIBAHJQFLggoYb7R5Pf +0C9FX0jiuF8DlE4P7mOadiTGJEzeZ2uOHmGrve7qKvrbTOMKXWNTrNDN22wf7XL2 +Q+gVJz/B/6FFIRtqXN3jWCI4QDKAwS1C8ZN7mKohcRHqvBwAlkteoDAHoYIQlJGY +x2dOxfK6Hmal6V7ZmFQSUNTCDIlg7Mguz8WaI0C1Wen+jJHUjiIkPz8xKF/gvddl +18iqHVGfZFSI1rTvfGeVO1vRwlwHoVV9HeXYEw9pAe0yxDLKKvY2brf8J8+qSWl2 +l7Unr89Ti8Yrm0EWQs/xFZpvQRkl7NG7WLIMJBCc4w3wOEvjpyklw7CfJaWGu6lM +sRlk4gECgYEA+LQhF0SaL6Vs36VVAMrTqj8EWyr4V7AB+xAr0aoGawKnSnnlOj4x +BSer4sulyQvL81rQnbekgkpWl05ZCcDp52+ATak+yBq3BvnX5SNjovcKQTuWhqLk +EMHK8SuaMKcp+jUTRdaohsmy+zUtNzkkVEzNMdR93i7+yS1ao6lMHtMCgYEAybFC +vTMhZITXmpvEHQ0qi0Ucgp/J6VQkPAhQMzoUHSIZbL6eXhaFmeQdD/ta9nIoOc3K +enDEenW3ABrz84fPNaRVnS5U7H5448ZxW1HskwbeDx88PjTbUkS+tT8ITF3PiwA3 +d+vOw8ngg8Msmve19Sh6ssSPLvIIfxwYpTxIxoECgYEAoSnxS6e8FuYnQGJeTC4j +re46P24AEqrPDcfz7WE12YCVshB9uBl3ILUNkOGRJFBNsPyHtby8kWXk6RXvYv+t +U7mQtkLXmUqekpmzCxy8w209KvqXV9YU3rsGbPRpbd/VtvtP6vDosrfgESPrkh6o +aSx/yCvACQwBNZL7apUZ69sCgYBAuUCohIr3veWOeOQTSpFXhgMjK/HYjabfGO/b +sIyZ2MJ98iHSIboX62skIM5M/c9I1XBfoGZ8wd/LCds1UGS/WxAaU67vAZr7xUfF +PWIEwJRsF+L2N3IWUXc9pI+eKhCbE6O5ORPuIo+I2Q4sYMekd6wASDGGqCbv221R +QSo9gQKBgCRhdvN6Jp0IFrpHtopSaSYUCek0rotWlmXMuX/n4k+3RLdX4/xNn5gV +gR8nvVvfzLz9wmsbERsKmnMILBIWZPD//2O9PXubcMp4Shifl0mKh9zzPvriiF/H +7E9UU53p9WScYAPW+BonpKnopoGCXxM2smTwE2bdxzPs3jDWM8lJ +-----END RSA PRIVATE KEY----- diff --git a/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/resources/oauth2/certs/client/client.p12 b/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/resources/oauth2/certs/client/client.p12 new file mode 100644 index 00000000..52ca2b62 Binary files /dev/null and b/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/resources/oauth2/certs/client/client.p12 differ diff --git a/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/resources/oauth2/certs/client/client.pem b/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/resources/oauth2/certs/client/client.pem new file mode 100644 index 00000000..7d3d8915 --- /dev/null +++ b/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/resources/oauth2/certs/client/client.pem @@ -0,0 +1,52 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEAw/GjXGUkP1iL5+RH0wruU0exDpUsdNp3qS1Qb5q3StyVB9gb +bH8bWWnzXZRpx5B7E6AQ9NzuDj5wvyT3MN8lmGMXSE3mNk+t/mvW8/a/3HB1FF5t +V5mO4eyM86N677UhtJY4QstQ2qp2C3jPFRsScyNfr3efuOK8E9Yy2omJWZcuAH9t +0DiolDkPG1MXSNkunGVU48oqT8bocy5boR68jhfI/YwDVrJRelO6VyPf2AJ8w3cH +Fyl6khhJXmDN6pXTd+5SiHsau2cEBMhDEV7eC4kRygGpP4NQKJ4fSFGaLMDjflQr +6WnLMNlbEuMtKHuaydNoh+Aqa42aslwKToq6UwIDAQABAoIBAHJQFLggoYb7R5Pf +0C9FX0jiuF8DlE4P7mOadiTGJEzeZ2uOHmGrve7qKvrbTOMKXWNTrNDN22wf7XL2 +Q+gVJz/B/6FFIRtqXN3jWCI4QDKAwS1C8ZN7mKohcRHqvBwAlkteoDAHoYIQlJGY +x2dOxfK6Hmal6V7ZmFQSUNTCDIlg7Mguz8WaI0C1Wen+jJHUjiIkPz8xKF/gvddl +18iqHVGfZFSI1rTvfGeVO1vRwlwHoVV9HeXYEw9pAe0yxDLKKvY2brf8J8+qSWl2 +l7Unr89Ti8Yrm0EWQs/xFZpvQRkl7NG7WLIMJBCc4w3wOEvjpyklw7CfJaWGu6lM +sRlk4gECgYEA+LQhF0SaL6Vs36VVAMrTqj8EWyr4V7AB+xAr0aoGawKnSnnlOj4x +BSer4sulyQvL81rQnbekgkpWl05ZCcDp52+ATak+yBq3BvnX5SNjovcKQTuWhqLk +EMHK8SuaMKcp+jUTRdaohsmy+zUtNzkkVEzNMdR93i7+yS1ao6lMHtMCgYEAybFC +vTMhZITXmpvEHQ0qi0Ucgp/J6VQkPAhQMzoUHSIZbL6eXhaFmeQdD/ta9nIoOc3K +enDEenW3ABrz84fPNaRVnS5U7H5448ZxW1HskwbeDx88PjTbUkS+tT8ITF3PiwA3 +d+vOw8ngg8Msmve19Sh6ssSPLvIIfxwYpTxIxoECgYEAoSnxS6e8FuYnQGJeTC4j +re46P24AEqrPDcfz7WE12YCVshB9uBl3ILUNkOGRJFBNsPyHtby8kWXk6RXvYv+t +U7mQtkLXmUqekpmzCxy8w209KvqXV9YU3rsGbPRpbd/VtvtP6vDosrfgESPrkh6o +aSx/yCvACQwBNZL7apUZ69sCgYBAuUCohIr3veWOeOQTSpFXhgMjK/HYjabfGO/b +sIyZ2MJ98iHSIboX62skIM5M/c9I1XBfoGZ8wd/LCds1UGS/WxAaU67vAZr7xUfF +PWIEwJRsF+L2N3IWUXc9pI+eKhCbE6O5ORPuIo+I2Q4sYMekd6wASDGGqCbv221R +QSo9gQKBgCRhdvN6Jp0IFrpHtopSaSYUCek0rotWlmXMuX/n4k+3RLdX4/xNn5gV +gR8nvVvfzLz9wmsbERsKmnMILBIWZPD//2O9PXubcMp4Shifl0mKh9zzPvriiF/H +7E9UU53p9WScYAPW+BonpKnopoGCXxM2smTwE2bdxzPs3jDWM8lJ +-----END RSA PRIVATE KEY----- +-----BEGIN CERTIFICATE----- +MIIEPzCCAicCFGwFUJczzeABS1iyOJRPkc8NXBvoMA0GCSqGSIb3DQEBCwUAMFsx +CzAJBgNVBAYTAkNBMRAwDgYDVQQIDAdPbnRhcmlvMQ8wDQYDVQQHDAZLYW5hdGEx +FzAVBgNVBAoMDlNvbGFjZSBTeXN0ZW1zMRAwDgYDVQQDDAdSb290IENBMB4XDTI0 +MDcxMDE1MzUyNFoXDTQ0MDcwNTE1MzUyNFowXTELMAkGA1UEBhMCQ0ExEDAOBgNV +BAgMB09udGFyaW8xDzANBgNVBAcMBkthbmF0YTEXMBUGA1UECgwOU29sYWNlIFN5 +c3RlbXMxEjAQBgNVBAMMCXNvbGNsaWVudDCCASIwDQYJKoZIhvcNAQEBBQADggEP +ADCCAQoCggEBAMPxo1xlJD9Yi+fkR9MK7lNHsQ6VLHTad6ktUG+at0rclQfYG2x/ +G1lp812UaceQexOgEPTc7g4+cL8k9zDfJZhjF0hN5jZPrf5r1vP2v9xwdRRebVeZ +juHsjPOjeu+1IbSWOELLUNqqdgt4zxUbEnMjX693n7jivBPWMtqJiVmXLgB/bdA4 +qJQ5DxtTF0jZLpxlVOPKKk/G6HMuW6EevI4XyP2MA1ayUXpTulcj39gCfMN3Bxcp +epIYSV5gzeqV03fuUoh7GrtnBATIQxFe3guJEcoBqT+DUCieH0hRmizA435UK+lp +yzDZWxLjLSh7msnTaIfgKmuNmrJcCk6KulMCAwEAATANBgkqhkiG9w0BAQsFAAOC +AgEAiMRofj8jnppDr+IOCdZTqSUemV/+5+IIYJkMpibPnM/WPQjn95GgxLhfNtAq +gaIOlN/IPEMJCZzCVSX/Fd+5YI10NBX7wBoFVrf0izSp40OAzynqlGrFMPdWQwAJ +9kyqSuZa7R8phZzRAH6IrdsQcrxr7qGc5yBCEXnLTbd5rCEXYh6Yvq5/CspYvuCJ +9kieXujn1XDt/bApFxkKJpOzkXZQxRDBKK8oYxH6u/0uN03M0yXnswiUVBjXEGZE +MpBud1qIOfDOnZeS2yxJRKzX9Q0tXg9CHT0CnQTTC9f31FfDYW/ro81pqnLuzlm7 +RIPb7M6ovgJgFUmvh8lSqz0wDugNfTZZX6QCa/x3kUlCtmxwbLsC4MYmzgy3wtYw +Bt6B8t9KzWCdLRbk+akGOX3nDn8NUpZhqgXyVUY10IxSIShueZ+IIwvdbKEXdIsz +QNyaU76zeD175gPVOb9upuV7R3gJXcbLRQ6gCNoGkZrnTXOjI4wpGFmWHXJOJTDS +nngQ9n1QiGBqYsEun+PRIod90ucwjTYuG88HSqTS8zsYQSf1qvQCG79Piq2osdMW +jxPzdK/bZDNBObSGl/gHkXy9YmZ2MFyQoEaVeRjPUYRcPquALMFYRB9HDBWlYDsb +GTgiYKVIs3FK90XnX7TzXyzH0GyzOCkC/47/dplWkP3WL+8= +-----END CERTIFICATE----- diff --git a/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/resources/oauth2/certs/keycloak/keycloak.crt b/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/resources/oauth2/certs/keycloak/keycloak.crt new file mode 100644 index 00000000..062f94b0 --- /dev/null +++ b/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/resources/oauth2/certs/keycloak/keycloak.crt @@ -0,0 +1,30 @@ +-----BEGIN CERTIFICATE----- +MIIFHzCCAwegAwIBAgIUbAVQlzPN4AFLWLI4lE+Rzw1cG+kwDQYJKoZIhvcNAQEL +BQAwWzELMAkGA1UEBhMCQ0ExEDAOBgNVBAgMB09udGFyaW8xDzANBgNVBAcMBkth +bmF0YTEXMBUGA1UECgwOU29sYWNlIFN5c3RlbXMxEDAOBgNVBAMMB1Jvb3QgQ0Ew +HhcNMjQwNzEwMTU0MzMyWhcNNDQwNzA1MTU0MzMyWjB2MQswCQYDVQQGEwJDQTEQ +MA4GA1UECAwHT250YXJpbzEPMA0GA1UEBwwGS2FuYXRhMRcwFQYDVQQKDA5Tb2xh +Y2UgU3lzdGVtczEXMBUGA1UECwwOU29sYWNlIFN5c3RlbXMxEjAQBgNVBAMMCXNv +bGJyb2tlcjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKuVDvCSYll7 +7eWyEmcDDunp+z/4KziZatEZYGNten2SRzfvhtP+uLJCbexjeJ8NAYtSILE1q1tD +lVAIItU6I2gUxKbJ8iUgX+VB92/35k314dKwvR7/pMivEj7joRdzE/qVszOJDZYu +K9n4JHzpzpQ4fc9JCDOHlDlGjQMNan5HHjlzWloUbTeDlaEK6lGLimenYkTiU/Jt +K9HS9Ep00E8IGNoTTuP7eUOnr+DxHSvdFJCarxe8t/TALkPgRyL/y14JOs9zG3e9 +AAblGWXXpexeLnKAUApJK/WX+bscWx9gdsbnAL0RSEO4hPX7BdAx5n3nwhbwWUYz +HCWT72HQs0sCAwEAAaOBvzCBvDCBuQYDVR0RBIGxMIGugglzb2xicm9rZXKCCnNv +bGJyb2tlcjGCC3NvbGJyb2tlcl8xgglsb2NhbGhvc3SCC3NvbGFjZU9BdXRoggtz +b2xhY2VvYXV0aIIMc29sYWNlb2F1dGgyghUqLnNvbGFjZV9pbnRlcm5hbF9uZXSC +ECouc29sYWNlX21zZ19uZXSCFCouc29sYWNlX21zZ19uZXR3b3JrhwR/AAABhxAA +AAAAAAAAAAAAAAAAAAABMA0GCSqGSIb3DQEBCwUAA4ICAQBJ8jD5hSKXPri4NmOx +S3Lh6bMbkq8JcIQesR7sgPFENy3UX+WuAY6Fjm9edXBdG+9OuqfBXoSHEA8jL+oU +gos1Lz/6Sy3kKGIe8E44zgas0CYH3veXJJeLMBaE8RKdKEaiEUT+Ka7OXhhOWDGj +n8QKo5um+ZUzsBzBF5B0zw/5kK02aDE9N/eqvAAm1fZqDC8txZD7i0dBa0rrQp8Q +yYZGQmobis7ban//MOxPnL/lpoWVkwn3HrGlUzhhoG8xwokI0pXGMCga4K3iWvyc +jTbICt8AyEyhwQqOpugzVQxRc7Qs3sTlkrN2bTy6mmS5WrYIYwCKfq9yeH9OR41Q +KVsHpcDrSCtVlwYe8TF/d+HkvFJudKj5VgXPatI3tRAFe1BxXWA0fNHn/Hla9QiC +sYyb3T9dyxW1XVTV9sLNcib8Q8Cew/JqchaemPNG+gek+/tc7lOQfdRNdoKcflPc +ko+2OvPZAoI5tg9kOLYn71KG3sHMJgU/42yZhbwOtPEGJ+AXH+TtIE1Twoum8C2X +qe5uMOuE7MziRD2Zsg6t0Wi7RgLMfz+nKNDWJ1YXHX5tBj+p6CxyKZ+6MUmnj1Cl +toLbj/FgNtJaey9bi1zVK2oy6r3oHn7sdI5NdMPFuRTYacX/Foq5KAeDo/MzN9pa +mnTgszi5F12OP3KbMvw6PyR8tQ== +-----END CERTIFICATE----- diff --git a/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/resources/oauth2/certs/keycloak/keycloak.csr b/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/resources/oauth2/certs/keycloak/keycloak.csr new file mode 100644 index 00000000..60032ebb --- /dev/null +++ b/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/resources/oauth2/certs/keycloak/keycloak.csr @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE REQUEST----- +MIIDjDCCAnQCAQAwdjELMAkGA1UEBhMCQ0ExEDAOBgNVBAgMB09udGFyaW8xDzAN +BgNVBAcMBkthbmF0YTEXMBUGA1UECgwOU29sYWNlIFN5c3RlbXMxFzAVBgNVBAsM +DlNvbGFjZSBTeXN0ZW1zMRIwEAYDVQQDDAlzb2xicm9rZXIwggEiMA0GCSqGSIb3 +DQEBAQUAA4IBDwAwggEKAoIBAQCrlQ7wkmJZe+3lshJnAw7p6fs/+Cs4mWrRGWBj +bXp9kkc374bT/riyQm3sY3ifDQGLUiCxNatbQ5VQCCLVOiNoFMSmyfIlIF/lQfdv +9+ZN9eHSsL0e/6TIrxI+46EXcxP6lbMziQ2WLivZ+CR86c6UOH3PSQgzh5Q5Ro0D +DWp+Rx45c1paFG03g5WhCupRi4pnp2JE4lPybSvR0vRKdNBPCBjaE07j+3lDp6/g +8R0r3RSQmq8XvLf0wC5D4Eci/8teCTrPcxt3vQAG5Rll16XsXi5ygFAKSSv1l/m7 +HFsfYHbG5wC9EUhDuIT1+wXQMeZ958IW8FlGMxwlk+9h0LNLAgMBAAGggdAwgc0G +CSqGSIb3DQEJDjGBvzCBvDCBuQYDVR0RBIGxMIGugglzb2xicm9rZXKCCnNvbGJy +b2tlcjGCC3NvbGJyb2tlcl8xgglsb2NhbGhvc3SCC3NvbGFjZU9BdXRoggtzb2xh +Y2VvYXV0aIIMc29sYWNlb2F1dGgyghUqLnNvbGFjZV9pbnRlcm5hbF9uZXSCECou +c29sYWNlX21zZ19uZXSCFCouc29sYWNlX21zZ19uZXR3b3JrhwR/AAABhxAAAAAA +AAAAAAAAAAAAAAABMA0GCSqGSIb3DQEBCwUAA4IBAQBzBBKrR+MCMi2lv4m5wFuK +vj8/idCcCQeVLhMFCgFu770FMAw2NLvYi0SB56S3J1FUrEkT8XPMfts8S8msrCot +UZGMJMCc5RHeI+A308+xz069Y0i4wo4ajK3CkBRXzoMFejlgzF0oMafOMx2Lp9Tl +k+s6peHvKdXa0KQIvn28DwO2sfRP4fqMy4borJRLYLOJEKGMCjo1ClsJNA4pZvFv +mSJCO4DlUfEn04+H18EOKNLs8ChQXsxKqKAk+1VKvtrvRByPhhfU1Jg0Smmipvcg +Z0uQseLKKZ2MYw6tCuvC1C+LbBtDd43GG1ayCg2G8OtNfKIHoCS0oVWsvUnENgUK +-----END CERTIFICATE REQUEST----- diff --git a/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/resources/oauth2/certs/keycloak/keycloak.key b/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/resources/oauth2/certs/keycloak/keycloak.key new file mode 100644 index 00000000..6d9102dd --- /dev/null +++ b/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/resources/oauth2/certs/keycloak/keycloak.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEoQIBAAKCAQEAq5UO8JJiWXvt5bISZwMO6en7P/grOJlq0RlgY216fZJHN++G +0/64skJt7GN4nw0Bi1IgsTWrW0OVUAgi1TojaBTEpsnyJSBf5UH3b/fmTfXh0rC9 +Hv+kyK8SPuOhF3MT+pWzM4kNli4r2fgkfOnOlDh9z0kIM4eUOUaNAw1qfkceOXNa +WhRtN4OVoQrqUYuKZ6diROJT8m0r0dL0SnTQTwgY2hNO4/t5Q6ev4PEdK90UkJqv +F7y39MAuQ+BHIv/LXgk6z3Mbd70ABuUZZdel7F4ucoBQCkkr9Zf5uxxbH2B2xucA +vRFIQ7iE9fsF0DHmfefCFvBZRjMcJZPvYdCzSwIDAQABAoIBAAm/RwD9n96rfqE8 +03TMpK0/IInKxFHLzVihk2syjfHSPH99+O/UGZPu2CXEpNaMO5k5iifm/5wIo9PP +EoOAcQB5pY5ADKR1SV1RuQfAUnH9VN3OMoAvT6Ii5+twrPcTD4B9vpdf4si0SMNy +KEh8U8LxzpvW70NWIWJ7koko2vLfaYVuvDSMZLXll9B9iz6ROYp1qNh0aYrscRC/ +uC2/ziAJzbMEhsPN59AF8upfnyBxEOjxhFJ9LzbsxNoZyRCCMuDu35TUB57q1TZm +rp8RdrqZ2qlknkZMbf5E06xyHBt4QgyzltivxbugShZB9iUe74Mjqyp4LTkAoy8J +U/P9cTkCgYEA4Tg+0mOvMpVE0x5nrV3z5uydNnYOWdDk3W8Q3JVKs5b7QQDFWtEs +stP6jZCNnQkrRnhwY2mlGnWUUJ0CEtZp5eWXHdHsrFL150FOBYBofVdFTZFKdHRk +CXn7zSXLULmrqBRptI0K7DQzAJ9y2agh3iATka22xuHUscm7qrKuqh0CgYEAwwgz +hkruZBO/+6VE2LBy1/ew5btZyaF/x85Q/AiBPfEO/ilT4cKV3eOOZtjpnsyF15U2 +p4ubluNbvUYlJ7IPTZEUWwiw94B5hksQXzUfi96zb9GDcaEfoYGsZ+fqEPVkqgzM +Z5rE8Db9QDnsd9uGdzSPMziEnLGqqNEd/lGolocCf1HRHQFRNVQq5dXMNd3FQ9Wg +H3ypZo06VeobbwSzN3AGaUA0B332f0Z3u42x9cAWlKIFHs7+kfwKutaOMzKksdPS +lBNBL7lqaeqYzr8w5sSh74s+PM4RekX3CoJ8OGAbE0D8KWpt0on8bIrNYeuwKJ2J +CZLiiIO3ho0PvB1GzC0CgYEAg//U/5tPZaSIV4Uv54jk8Y7Ox23aA0Gu/kiBP1Ny +Rb4Va6gFAdN1I0yUYL+Gvtel7pcq+pLep20R9jS3iPpWqST8JfDn9Vua5G2Bky6d +P0lnINMop4tpoSHm0hyAqyGrE/y9i5GQoRRWq1WI2kZV5/BGy2ABQRxuaPu/1RTn +iZkCgYB92CW0tnV1PDr4fRbFMQ/KnAHCaQHBjnfyx1pYAn5Km+iC7OiwT9/JFTAx +fic17YAogeOOWnroiH7uUtJRLbo1kyCS1RDIFW0LsreXeGkQ4147cbyQxHORu7s0 +SU9voGpGV26zX0Ik0VbefsonMDZrQhoxign568DMLIMGfp/Sqg== +-----END RSA PRIVATE KEY----- diff --git a/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/resources/oauth2/certs/keycloak/keycloak.pem b/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/resources/oauth2/certs/keycloak/keycloak.pem new file mode 100644 index 00000000..d6020f39 --- /dev/null +++ b/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/resources/oauth2/certs/keycloak/keycloak.pem @@ -0,0 +1,57 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEoQIBAAKCAQEAq5UO8JJiWXvt5bISZwMO6en7P/grOJlq0RlgY216fZJHN++G +0/64skJt7GN4nw0Bi1IgsTWrW0OVUAgi1TojaBTEpsnyJSBf5UH3b/fmTfXh0rC9 +Hv+kyK8SPuOhF3MT+pWzM4kNli4r2fgkfOnOlDh9z0kIM4eUOUaNAw1qfkceOXNa +WhRtN4OVoQrqUYuKZ6diROJT8m0r0dL0SnTQTwgY2hNO4/t5Q6ev4PEdK90UkJqv +F7y39MAuQ+BHIv/LXgk6z3Mbd70ABuUZZdel7F4ucoBQCkkr9Zf5uxxbH2B2xucA +vRFIQ7iE9fsF0DHmfefCFvBZRjMcJZPvYdCzSwIDAQABAoIBAAm/RwD9n96rfqE8 +03TMpK0/IInKxFHLzVihk2syjfHSPH99+O/UGZPu2CXEpNaMO5k5iifm/5wIo9PP +EoOAcQB5pY5ADKR1SV1RuQfAUnH9VN3OMoAvT6Ii5+twrPcTD4B9vpdf4si0SMNy +KEh8U8LxzpvW70NWIWJ7koko2vLfaYVuvDSMZLXll9B9iz6ROYp1qNh0aYrscRC/ +uC2/ziAJzbMEhsPN59AF8upfnyBxEOjxhFJ9LzbsxNoZyRCCMuDu35TUB57q1TZm +rp8RdrqZ2qlknkZMbf5E06xyHBt4QgyzltivxbugShZB9iUe74Mjqyp4LTkAoy8J +U/P9cTkCgYEA4Tg+0mOvMpVE0x5nrV3z5uydNnYOWdDk3W8Q3JVKs5b7QQDFWtEs +stP6jZCNnQkrRnhwY2mlGnWUUJ0CEtZp5eWXHdHsrFL150FOBYBofVdFTZFKdHRk +CXn7zSXLULmrqBRptI0K7DQzAJ9y2agh3iATka22xuHUscm7qrKuqh0CgYEAwwgz +hkruZBO/+6VE2LBy1/ew5btZyaF/x85Q/AiBPfEO/ilT4cKV3eOOZtjpnsyF15U2 +p4ubluNbvUYlJ7IPTZEUWwiw94B5hksQXzUfi96zb9GDcaEfoYGsZ+fqEPVkqgzM +Z5rE8Db9QDnsd9uGdzSPMziEnLGqqNEd/lGolocCf1HRHQFRNVQq5dXMNd3FQ9Wg +H3ypZo06VeobbwSzN3AGaUA0B332f0Z3u42x9cAWlKIFHs7+kfwKutaOMzKksdPS +lBNBL7lqaeqYzr8w5sSh74s+PM4RekX3CoJ8OGAbE0D8KWpt0on8bIrNYeuwKJ2J +CZLiiIO3ho0PvB1GzC0CgYEAg//U/5tPZaSIV4Uv54jk8Y7Ox23aA0Gu/kiBP1Ny +Rb4Va6gFAdN1I0yUYL+Gvtel7pcq+pLep20R9jS3iPpWqST8JfDn9Vua5G2Bky6d +P0lnINMop4tpoSHm0hyAqyGrE/y9i5GQoRRWq1WI2kZV5/BGy2ABQRxuaPu/1RTn +iZkCgYB92CW0tnV1PDr4fRbFMQ/KnAHCaQHBjnfyx1pYAn5Km+iC7OiwT9/JFTAx +fic17YAogeOOWnroiH7uUtJRLbo1kyCS1RDIFW0LsreXeGkQ4147cbyQxHORu7s0 +SU9voGpGV26zX0Ik0VbefsonMDZrQhoxign568DMLIMGfp/Sqg== +-----END RSA PRIVATE KEY----- +-----BEGIN CERTIFICATE----- +MIIFHzCCAwegAwIBAgIUbAVQlzPN4AFLWLI4lE+Rzw1cG+kwDQYJKoZIhvcNAQEL +BQAwWzELMAkGA1UEBhMCQ0ExEDAOBgNVBAgMB09udGFyaW8xDzANBgNVBAcMBkth +bmF0YTEXMBUGA1UECgwOU29sYWNlIFN5c3RlbXMxEDAOBgNVBAMMB1Jvb3QgQ0Ew +HhcNMjQwNzEwMTU0MzMyWhcNNDQwNzA1MTU0MzMyWjB2MQswCQYDVQQGEwJDQTEQ +MA4GA1UECAwHT250YXJpbzEPMA0GA1UEBwwGS2FuYXRhMRcwFQYDVQQKDA5Tb2xh +Y2UgU3lzdGVtczEXMBUGA1UECwwOU29sYWNlIFN5c3RlbXMxEjAQBgNVBAMMCXNv +bGJyb2tlcjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKuVDvCSYll7 +7eWyEmcDDunp+z/4KziZatEZYGNten2SRzfvhtP+uLJCbexjeJ8NAYtSILE1q1tD +lVAIItU6I2gUxKbJ8iUgX+VB92/35k314dKwvR7/pMivEj7joRdzE/qVszOJDZYu +K9n4JHzpzpQ4fc9JCDOHlDlGjQMNan5HHjlzWloUbTeDlaEK6lGLimenYkTiU/Jt +K9HS9Ep00E8IGNoTTuP7eUOnr+DxHSvdFJCarxe8t/TALkPgRyL/y14JOs9zG3e9 +AAblGWXXpexeLnKAUApJK/WX+bscWx9gdsbnAL0RSEO4hPX7BdAx5n3nwhbwWUYz +HCWT72HQs0sCAwEAAaOBvzCBvDCBuQYDVR0RBIGxMIGugglzb2xicm9rZXKCCnNv +bGJyb2tlcjGCC3NvbGJyb2tlcl8xgglsb2NhbGhvc3SCC3NvbGFjZU9BdXRoggtz +b2xhY2VvYXV0aIIMc29sYWNlb2F1dGgyghUqLnNvbGFjZV9pbnRlcm5hbF9uZXSC +ECouc29sYWNlX21zZ19uZXSCFCouc29sYWNlX21zZ19uZXR3b3JrhwR/AAABhxAA +AAAAAAAAAAAAAAAAAAABMA0GCSqGSIb3DQEBCwUAA4ICAQBJ8jD5hSKXPri4NmOx +S3Lh6bMbkq8JcIQesR7sgPFENy3UX+WuAY6Fjm9edXBdG+9OuqfBXoSHEA8jL+oU +gos1Lz/6Sy3kKGIe8E44zgas0CYH3veXJJeLMBaE8RKdKEaiEUT+Ka7OXhhOWDGj +n8QKo5um+ZUzsBzBF5B0zw/5kK02aDE9N/eqvAAm1fZqDC8txZD7i0dBa0rrQp8Q +yYZGQmobis7ban//MOxPnL/lpoWVkwn3HrGlUzhhoG8xwokI0pXGMCga4K3iWvyc +jTbICt8AyEyhwQqOpugzVQxRc7Qs3sTlkrN2bTy6mmS5WrYIYwCKfq9yeH9OR41Q +KVsHpcDrSCtVlwYe8TF/d+HkvFJudKj5VgXPatI3tRAFe1BxXWA0fNHn/Hla9QiC +sYyb3T9dyxW1XVTV9sLNcib8Q8Cew/JqchaemPNG+gek+/tc7lOQfdRNdoKcflPc +ko+2OvPZAoI5tg9kOLYn71KG3sHMJgU/42yZhbwOtPEGJ+AXH+TtIE1Twoum8C2X +qe5uMOuE7MziRD2Zsg6t0Wi7RgLMfz+nKNDWJ1YXHX5tBj+p6CxyKZ+6MUmnj1Cl +toLbj/FgNtJaey9bi1zVK2oy6r3oHn7sdI5NdMPFuRTYacX/Foq5KAeDo/MzN9pa +mnTgszi5F12OP3KbMvw6PyR8tQ== +-----END CERTIFICATE----- diff --git a/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/resources/oauth2/certs/keycloak_san.conf b/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/resources/oauth2/certs/keycloak_san.conf new file mode 100644 index 00000000..be980a73 --- /dev/null +++ b/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/resources/oauth2/certs/keycloak_san.conf @@ -0,0 +1,30 @@ +[ req ] +req_extensions = req_ext +distinguished_name = req_distinguished_name +prompt = no + +[ req_distinguished_name ] +C = CA +ST = Ontario +L = Kanata +O = Solace Systems +OU = Solace Systems +CN = solbroker + +[ req_ext ] +subjectAltName = @alt_names + +[ alt_names ] +DNS.1 = solbroker +DNS.2 = solbroker1 +DNS.3 = solbroker_1 +DNS.4 = localhost +DNS.5 = solaceOAuth +DNS.6 = solaceoauth +DNS.7 = solaceoauth2 +DNS.8 = *.solace_internal_net +DNS.9 = *.solace_msg_net +DNS.10 = *.solace_msg_network +IP.1 = 127.0.0.1 +IP.2 = 0:0:0:0:0:0:0:1 +# ... (add more SAN entries) \ No newline at end of file diff --git a/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/resources/oauth2/certs/rootCA/rootCA.crt b/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/resources/oauth2/certs/rootCA/rootCA.crt new file mode 100644 index 00000000..43fd900d --- /dev/null +++ b/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/resources/oauth2/certs/rootCA/rootCA.crt @@ -0,0 +1,32 @@ +-----BEGIN CERTIFICATE----- +MIIFlzCCA3+gAwIBAgIUU1biQS/fFJ8bcYNdXYXeaJ7htJIwDQYJKoZIhvcNAQEL +BQAwWzELMAkGA1UEBhMCQ0ExEDAOBgNVBAgMB09udGFyaW8xDzANBgNVBAcMBkth +bmF0YTEXMBUGA1UECgwOU29sYWNlIFN5c3RlbXMxEDAOBgNVBAMMB1Jvb3QgQ0Ew +HhcNMjQwNzEwMTQ0NzM2WhcNNDQwNzA1MTQ0NzM2WjBbMQswCQYDVQQGEwJDQTEQ +MA4GA1UECAwHT250YXJpbzEPMA0GA1UEBwwGS2FuYXRhMRcwFQYDVQQKDA5Tb2xh +Y2UgU3lzdGVtczEQMA4GA1UEAwwHUm9vdCBDQTCCAiIwDQYJKoZIhvcNAQEBBQAD +ggIPADCCAgoCggIBAJXSAsGmhmUYSnPoNNDeV09mh5v2ZnMGJuP7+y2cNzaoLdPM +LDV1As/gfnF6n7hGWXj+ZFxOmpMwBEBNnfQVHtzgYWdIW9p34/fdznJr3PIKeC+K +39s3PacyIIriX8WxD6T+WAO7RjzrbNLq+2XB3sWU3BR2Y0mFCP/u0iEJ2QratWjr +A+Ers85jEGYlAsk5hL9D2jErVzluuW3Th+Rl/ov4ev1NE6vu/tm+k7I4PdTSA0PV +3T8FrzS/KQK7aBnU982hhB1lHWSWyJl3IOhPE5VzghBhDb0nytn0Y8kTh7+qmSC2 +8LdKwPt16pyvcamCBInxJnQFNQitlvTfTPLDGs0YTD47gBR5CTWLlr2u8grlxviY +T0xALNtf8RqcbMpizAt6+9RJsFKFOip2xKbVuia5KH96TYqMFmPSFs72jJBr2bC1 +/JpyokdUh6gquZfmN18igdu8UB58sK03wYdVort+6nLBax8JNYBvj2+hy8r3HVsi +5xdaZofj3vPyFBC7GHrn7X4cUiff0RSncJC8Fr0LRb3Pi5sXNmN4wFAlJIC/Wd67 +QWouvoU1zUe9n8IB0yXGJcJ9u7CMZDxgAj9EAh4xkR0q7gxT5XSBpm4ZhOX35GwO +50/cZ3zmNsQ/6d/illGwnP7bc6G8cCWKErDquLm5dI2P9OEUdOPFuIwkmLZFAgMB +AAGjUzBRMB0GA1UdDgQWBBQWSokRHFfinJUJY8moOtt3Mt2K/TAfBgNVHSMEGDAW +gBQWSokRHFfinJUJY8moOtt3Mt2K/TAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3 +DQEBCwUAA4ICAQCSJvzYL7u/Nv0Eq9xBvotNN7Dl1KrgqbSzz0Q5jV68vIZiL9n0 +HdP9oe1mQZeYr1DrTnysnlMjU1s/uoMorNu2cd1LBvUKSrjhmeAGg4g2JYBUoeJf +C5kpFWaB3i5IksY8D0PlIsX3jufv35c4aP4NlUzcESu6cr+lO4K/GNV4cS5ACiaF +giPhJvWjW9n/yxQXTki/294RRsEqTxQzcHVC5ZSBRdiMti2BJoV/CgFxiZhKaFPy +dHOX8iHiI55mvLx+UisFc+YsJSMjuL7EjSJJQQVWIa+H7g23jenqbar0shstzfhE +NoPBaB65o9c6qKCruajlcjZBxKuGlgP4wmIZMkFWwktHK5JJorLv9A29EpZp1fFs +VE6kSNQxebbIvgZCqNE2bOTMfGspKVxDENOXDNTNgsf/lArtQk1+PMwjW1zwFGGn +tiH3LbJtLgeeX7HnB80lsohD5d+6/v/xxrB6aWRlpQGT+271trZdR/pixh2dhsPg +92D0oyR6CQTwuE1CUIthIonN1m+PtMKnpodoVsRkMwMOshcMWzmWVNNNIy5BuEgK +Lhh30MdiiwlpLV5gkcnygd4hkMV2VO9j5sFllo5ttnkQ6OZKmNoAqcYWIHCW2sh8 +rDYFzwes5qhK/K+qYLbBroHli14/p6AZRAlkcS8Vc5ew40naHn1Dd7EKRA== +-----END CERTIFICATE----- diff --git a/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/resources/oauth2/certs/rootCA/rootCA.der b/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/resources/oauth2/certs/rootCA/rootCA.der new file mode 100644 index 00000000..ca4a7fe1 Binary files /dev/null and b/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/resources/oauth2/certs/rootCA/rootCA.der differ diff --git a/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/resources/oauth2/certs/rootCA/rootCA.key b/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/resources/oauth2/certs/rootCA/rootCA.key new file mode 100644 index 00000000..58cb5363 --- /dev/null +++ b/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/resources/oauth2/certs/rootCA/rootCA.key @@ -0,0 +1,51 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIJKAIBAAKCAgEAldICwaaGZRhKc+g00N5XT2aHm/ZmcwYm4/v7LZw3Nqgt08ws +NXUCz+B+cXqfuEZZeP5kXE6akzAEQE2d9BUe3OBhZ0hb2nfj993Ocmvc8gp4L4rf +2zc9pzIgiuJfxbEPpP5YA7tGPOts0ur7ZcHexZTcFHZjSYUI/+7SIQnZCtq1aOsD +4SuzzmMQZiUCyTmEv0PaMStXOW65bdOH5GX+i/h6/U0Tq+7+2b6Tsjg91NIDQ9Xd +PwWvNL8pArtoGdT3zaGEHWUdZJbImXcg6E8TlXOCEGENvSfK2fRjyROHv6qZILbw +t0rA+3XqnK9xqYIEifEmdAU1CK2W9N9M8sMazRhMPjuAFHkJNYuWva7yCuXG+JhP +TEAs21/xGpxsymLMC3r71EmwUoU6KnbEptW6Jrkof3pNiowWY9IWzvaMkGvZsLX8 +mnKiR1SHqCq5l+Y3XyKB27xQHnywrTfBh1Wiu37qcsFrHwk1gG+Pb6HLyvcdWyLn +F1pmh+Pe8/IUELsYeuftfhxSJ9/RFKdwkLwWvQtFvc+Lmxc2Y3jAUCUkgL9Z3rtB +ai6+hTXNR72fwgHTJcYlwn27sIxkPGACP0QCHjGRHSruDFPldIGmbhmE5ffkbA7n +T9xnfOY2xD/p3+KWUbCc/ttzobxwJYoSsOq4ubl0jY/04RR048W4jCSYtkUCAwEA +AQKCAgAI53Vgevwrz/jE0L0q2LwJrQdMPqWyGmB/Vj+EY29ooTAwEUdjWfPz1NzO +88HAWvYAWeYvEkDflI/8HmDP2918tR003TkQT+XNmnIlnMGB5Rtlf/Rz++F/KVyD +xJZ6kl5iqPckKaIwBrHuCycr0gziY0l9MdgOy4hQZao5anNq0LrNZIJThJxoHL0h +xPtYaEG6eFbkazYA5NLCczr1WRZ6zSbKHSWZJ1ggKtJuWidamay2AGTo1PanxOC0 +F91FA8JCh2HpuVO44blEXa8n/2Mjk7zcKlh9sHq+32Z60d1Uh9gX+KdvzVKQ3141 +N7wineaVKC7n4FNZk6+QGCFjoDLcfFeoo7PKykELzSgbNuIgCJw+Hgt0fvnipSWF +SXhNh9lCjXeEOKV3Joy5cuQLRDg6oKXw+004iwKlYjU2xjvtoVGRmAuiBXAmSRoC +W/VtkFpThmi3yKg5we0WRrEpN6oJaugWimF24YSuj2jwDYeno3vRpWHytmc/+PeU +QF5i6fF5qSSgWpc2eb1ipbG+tNIKEC9tJaBJWFoFHs92hiVU6RrIYVp2aTA2RK67 +OASRSeXeYgNgYqnBEk7JaVNT+35WlO/hJS0PO6wYIbyFtkDMrGqpeSv705qz/eVq +1w8R1C5nDfFSSgThBHsYtm89Rr6oUAFlBlj+AX32csPkTA/vAQKCAQEAx7UQD7cE +ye75k0qHus8nn7iF0kwGmB7xG1IJrMH4C/9E5AA5TwPhLmEsN9lUCr4QI8Whi+VG +e0im8CxfDDIk5YMcVNxHaAviHGHGw/Ve56Qlgb9aI6zI8ODKg1UJwAUmLUv69ZRJ +zbESG8uxX/1dd+pMYNuKDsuVJ2t9bHrWiOBrvaw2/R+5Mkk4BoVYy59q3u7XozBQ +4Po99JYQcdWaBKI5prxQkgNZ5NusDLmVIxA6FRclj2WWxCG3/AJY6h/oryqUvj9R +LFz7N0TcWjWpoDElGAGQ24QVkiEXl+No1KHfV8c2t8q3rDqjEwGHLfSIp+eLWUui +oUDP7/ysMPnbBQKCAQEAwA0XvCfOBXntN7qKBFkd2i7eaaGRm0dRdGSKyqAK6yfY +ONwT5yx9KzbZVV1phDtTdAatvRmaWQX5B92fNUyxPjl4E8CwkFPEosz9Vje7vIdF +aQI/QTkHdFu8VfKkTuDhbrO7yoY8jcS0hAo8azuFR8NQ7huVKMX5OoqmTr3iKxhX +ByOJjk0cyYnwicH7gCiGJatEv55U4hUj8zPSn0V/tYuTuEB/kznKtPpKVu9sIg3D +aOgSYSY0VMoxTnV2TRyiREKCkkOSbCD4Jsffxym2HgmEGZIDHzfz8PGsoANAfGyC +4xisxnNHsr17h2+QRRXAxCVKyoq5Y+Q5pRMnZebSQQKCAQAjKYMZcTz7nQL+Zwn8 +30p3udJ+E3q5wADtsYUYkNJuslRb3Jo6ilFUjDFv5+j/NzW6RqrJ6eV+AW27LeTS +TeXnLy2G134PGMCIBMMtb391Q5aDAMELNPnwR3QAqbFcyMtPAGjAYoqYF8w7bqLd +ZsvVOECYcS8eqcOqPCfKONqbIQB3VeIcsUA3VWLy6vmWaIw1klIPXotvAUB1VxKw +KE7E8Bc8fz2kZ8ilHfRuDSLwGIRRgFDRra5c/B7b1UH4fwPGC7ZCxP0y1XA56/rs +OzSRivWgA26Q5/GwV/lCefzUK2gamW3N8Hhkb7KUvxkhA0QoZAFKCKIqyDGUbKWY +vfVNAoIBAAsmd3NQKFD/FDvBE9ROzEHnqLgfTlHioSMN11UOV7Pxe0dJ18n7NkU1 +CQdAxiiMPTsmTB4Hh4OVqjC/uEei7UN8mLEk5dtrUaZWGntP/xFiFTCUldGWmw0x +akzfKpT9z3ja7JNEme1tN0HXSky7hvB0sZUxesaEQAUbGa7GrYPtBNiAQrNFXN+C +p7mHzq9RKwCy4enyKmF58r1jC959bX2/3dK7w+xrVY6OXZSQkAmmHOtRVgfX7P/j +QVuZzEWL3QvzhJszWyP2AhJWVnK8xDsYOFg3twCwAfTCQ1CC/9J5hlvjCdz3wnjp +MWvamVi5e5inxaDezwaysHoaE5aCAUECggEBAJjTpus8tJ0LEDAwO2J9N20NSp9i +IMw1rpgkLrYr7/6zAChxlyCb3jndHzP+XLkbZeDmUzXlz10lwswYUaN89rFz8DBf +JzmzTI8AeLMlNlP/tptj4U53ex9OgjHjy84YclqfdajuQYynM5S1a5MBGbFk0Kt9 +iV/N6OMtFtLQAgxtcMy6Wy/t5IgTWXfJx/XGtT/wS5a8IMojNcH/R/QyZM34IuLL +FE6pCrdfQnBofTtp6shR2iDjdGFRm5N+KO8psxcQBX3cqabNAgNEPp4zbdW5F55y +9xG5Pn18UReo6aCwAlPANxqj3u9wjShtgEDH+YP4kJ/nUzDw9LjQWdBaF70= +-----END RSA PRIVATE KEY----- diff --git a/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/resources/oauth2/certs/rootCA/rootCA.pem b/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/resources/oauth2/certs/rootCA/rootCA.pem new file mode 100644 index 00000000..c43bc76d --- /dev/null +++ b/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/resources/oauth2/certs/rootCA/rootCA.pem @@ -0,0 +1,83 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIJKAIBAAKCAgEAldICwaaGZRhKc+g00N5XT2aHm/ZmcwYm4/v7LZw3Nqgt08ws +NXUCz+B+cXqfuEZZeP5kXE6akzAEQE2d9BUe3OBhZ0hb2nfj993Ocmvc8gp4L4rf +2zc9pzIgiuJfxbEPpP5YA7tGPOts0ur7ZcHexZTcFHZjSYUI/+7SIQnZCtq1aOsD +4SuzzmMQZiUCyTmEv0PaMStXOW65bdOH5GX+i/h6/U0Tq+7+2b6Tsjg91NIDQ9Xd +PwWvNL8pArtoGdT3zaGEHWUdZJbImXcg6E8TlXOCEGENvSfK2fRjyROHv6qZILbw +t0rA+3XqnK9xqYIEifEmdAU1CK2W9N9M8sMazRhMPjuAFHkJNYuWva7yCuXG+JhP +TEAs21/xGpxsymLMC3r71EmwUoU6KnbEptW6Jrkof3pNiowWY9IWzvaMkGvZsLX8 +mnKiR1SHqCq5l+Y3XyKB27xQHnywrTfBh1Wiu37qcsFrHwk1gG+Pb6HLyvcdWyLn +F1pmh+Pe8/IUELsYeuftfhxSJ9/RFKdwkLwWvQtFvc+Lmxc2Y3jAUCUkgL9Z3rtB +ai6+hTXNR72fwgHTJcYlwn27sIxkPGACP0QCHjGRHSruDFPldIGmbhmE5ffkbA7n +T9xnfOY2xD/p3+KWUbCc/ttzobxwJYoSsOq4ubl0jY/04RR048W4jCSYtkUCAwEA +AQKCAgAI53Vgevwrz/jE0L0q2LwJrQdMPqWyGmB/Vj+EY29ooTAwEUdjWfPz1NzO +88HAWvYAWeYvEkDflI/8HmDP2918tR003TkQT+XNmnIlnMGB5Rtlf/Rz++F/KVyD +xJZ6kl5iqPckKaIwBrHuCycr0gziY0l9MdgOy4hQZao5anNq0LrNZIJThJxoHL0h +xPtYaEG6eFbkazYA5NLCczr1WRZ6zSbKHSWZJ1ggKtJuWidamay2AGTo1PanxOC0 +F91FA8JCh2HpuVO44blEXa8n/2Mjk7zcKlh9sHq+32Z60d1Uh9gX+KdvzVKQ3141 +N7wineaVKC7n4FNZk6+QGCFjoDLcfFeoo7PKykELzSgbNuIgCJw+Hgt0fvnipSWF +SXhNh9lCjXeEOKV3Joy5cuQLRDg6oKXw+004iwKlYjU2xjvtoVGRmAuiBXAmSRoC +W/VtkFpThmi3yKg5we0WRrEpN6oJaugWimF24YSuj2jwDYeno3vRpWHytmc/+PeU +QF5i6fF5qSSgWpc2eb1ipbG+tNIKEC9tJaBJWFoFHs92hiVU6RrIYVp2aTA2RK67 +OASRSeXeYgNgYqnBEk7JaVNT+35WlO/hJS0PO6wYIbyFtkDMrGqpeSv705qz/eVq +1w8R1C5nDfFSSgThBHsYtm89Rr6oUAFlBlj+AX32csPkTA/vAQKCAQEAx7UQD7cE +ye75k0qHus8nn7iF0kwGmB7xG1IJrMH4C/9E5AA5TwPhLmEsN9lUCr4QI8Whi+VG +e0im8CxfDDIk5YMcVNxHaAviHGHGw/Ve56Qlgb9aI6zI8ODKg1UJwAUmLUv69ZRJ +zbESG8uxX/1dd+pMYNuKDsuVJ2t9bHrWiOBrvaw2/R+5Mkk4BoVYy59q3u7XozBQ +4Po99JYQcdWaBKI5prxQkgNZ5NusDLmVIxA6FRclj2WWxCG3/AJY6h/oryqUvj9R +LFz7N0TcWjWpoDElGAGQ24QVkiEXl+No1KHfV8c2t8q3rDqjEwGHLfSIp+eLWUui +oUDP7/ysMPnbBQKCAQEAwA0XvCfOBXntN7qKBFkd2i7eaaGRm0dRdGSKyqAK6yfY +ONwT5yx9KzbZVV1phDtTdAatvRmaWQX5B92fNUyxPjl4E8CwkFPEosz9Vje7vIdF +aQI/QTkHdFu8VfKkTuDhbrO7yoY8jcS0hAo8azuFR8NQ7huVKMX5OoqmTr3iKxhX +ByOJjk0cyYnwicH7gCiGJatEv55U4hUj8zPSn0V/tYuTuEB/kznKtPpKVu9sIg3D +aOgSYSY0VMoxTnV2TRyiREKCkkOSbCD4Jsffxym2HgmEGZIDHzfz8PGsoANAfGyC +4xisxnNHsr17h2+QRRXAxCVKyoq5Y+Q5pRMnZebSQQKCAQAjKYMZcTz7nQL+Zwn8 +30p3udJ+E3q5wADtsYUYkNJuslRb3Jo6ilFUjDFv5+j/NzW6RqrJ6eV+AW27LeTS +TeXnLy2G134PGMCIBMMtb391Q5aDAMELNPnwR3QAqbFcyMtPAGjAYoqYF8w7bqLd +ZsvVOECYcS8eqcOqPCfKONqbIQB3VeIcsUA3VWLy6vmWaIw1klIPXotvAUB1VxKw +KE7E8Bc8fz2kZ8ilHfRuDSLwGIRRgFDRra5c/B7b1UH4fwPGC7ZCxP0y1XA56/rs +OzSRivWgA26Q5/GwV/lCefzUK2gamW3N8Hhkb7KUvxkhA0QoZAFKCKIqyDGUbKWY +vfVNAoIBAAsmd3NQKFD/FDvBE9ROzEHnqLgfTlHioSMN11UOV7Pxe0dJ18n7NkU1 +CQdAxiiMPTsmTB4Hh4OVqjC/uEei7UN8mLEk5dtrUaZWGntP/xFiFTCUldGWmw0x +akzfKpT9z3ja7JNEme1tN0HXSky7hvB0sZUxesaEQAUbGa7GrYPtBNiAQrNFXN+C +p7mHzq9RKwCy4enyKmF58r1jC959bX2/3dK7w+xrVY6OXZSQkAmmHOtRVgfX7P/j +QVuZzEWL3QvzhJszWyP2AhJWVnK8xDsYOFg3twCwAfTCQ1CC/9J5hlvjCdz3wnjp +MWvamVi5e5inxaDezwaysHoaE5aCAUECggEBAJjTpus8tJ0LEDAwO2J9N20NSp9i +IMw1rpgkLrYr7/6zAChxlyCb3jndHzP+XLkbZeDmUzXlz10lwswYUaN89rFz8DBf +JzmzTI8AeLMlNlP/tptj4U53ex9OgjHjy84YclqfdajuQYynM5S1a5MBGbFk0Kt9 +iV/N6OMtFtLQAgxtcMy6Wy/t5IgTWXfJx/XGtT/wS5a8IMojNcH/R/QyZM34IuLL +FE6pCrdfQnBofTtp6shR2iDjdGFRm5N+KO8psxcQBX3cqabNAgNEPp4zbdW5F55y +9xG5Pn18UReo6aCwAlPANxqj3u9wjShtgEDH+YP4kJ/nUzDw9LjQWdBaF70= +-----END RSA PRIVATE KEY----- +-----BEGIN CERTIFICATE----- +MIIFlzCCA3+gAwIBAgIUU1biQS/fFJ8bcYNdXYXeaJ7htJIwDQYJKoZIhvcNAQEL +BQAwWzELMAkGA1UEBhMCQ0ExEDAOBgNVBAgMB09udGFyaW8xDzANBgNVBAcMBkth +bmF0YTEXMBUGA1UECgwOU29sYWNlIFN5c3RlbXMxEDAOBgNVBAMMB1Jvb3QgQ0Ew +HhcNMjQwNzEwMTQ0NzM2WhcNNDQwNzA1MTQ0NzM2WjBbMQswCQYDVQQGEwJDQTEQ +MA4GA1UECAwHT250YXJpbzEPMA0GA1UEBwwGS2FuYXRhMRcwFQYDVQQKDA5Tb2xh +Y2UgU3lzdGVtczEQMA4GA1UEAwwHUm9vdCBDQTCCAiIwDQYJKoZIhvcNAQEBBQAD +ggIPADCCAgoCggIBAJXSAsGmhmUYSnPoNNDeV09mh5v2ZnMGJuP7+y2cNzaoLdPM +LDV1As/gfnF6n7hGWXj+ZFxOmpMwBEBNnfQVHtzgYWdIW9p34/fdznJr3PIKeC+K +39s3PacyIIriX8WxD6T+WAO7RjzrbNLq+2XB3sWU3BR2Y0mFCP/u0iEJ2QratWjr +A+Ers85jEGYlAsk5hL9D2jErVzluuW3Th+Rl/ov4ev1NE6vu/tm+k7I4PdTSA0PV +3T8FrzS/KQK7aBnU982hhB1lHWSWyJl3IOhPE5VzghBhDb0nytn0Y8kTh7+qmSC2 +8LdKwPt16pyvcamCBInxJnQFNQitlvTfTPLDGs0YTD47gBR5CTWLlr2u8grlxviY +T0xALNtf8RqcbMpizAt6+9RJsFKFOip2xKbVuia5KH96TYqMFmPSFs72jJBr2bC1 +/JpyokdUh6gquZfmN18igdu8UB58sK03wYdVort+6nLBax8JNYBvj2+hy8r3HVsi +5xdaZofj3vPyFBC7GHrn7X4cUiff0RSncJC8Fr0LRb3Pi5sXNmN4wFAlJIC/Wd67 +QWouvoU1zUe9n8IB0yXGJcJ9u7CMZDxgAj9EAh4xkR0q7gxT5XSBpm4ZhOX35GwO +50/cZ3zmNsQ/6d/illGwnP7bc6G8cCWKErDquLm5dI2P9OEUdOPFuIwkmLZFAgMB +AAGjUzBRMB0GA1UdDgQWBBQWSokRHFfinJUJY8moOtt3Mt2K/TAfBgNVHSMEGDAW +gBQWSokRHFfinJUJY8moOtt3Mt2K/TAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3 +DQEBCwUAA4ICAQCSJvzYL7u/Nv0Eq9xBvotNN7Dl1KrgqbSzz0Q5jV68vIZiL9n0 +HdP9oe1mQZeYr1DrTnysnlMjU1s/uoMorNu2cd1LBvUKSrjhmeAGg4g2JYBUoeJf +C5kpFWaB3i5IksY8D0PlIsX3jufv35c4aP4NlUzcESu6cr+lO4K/GNV4cS5ACiaF +giPhJvWjW9n/yxQXTki/294RRsEqTxQzcHVC5ZSBRdiMti2BJoV/CgFxiZhKaFPy +dHOX8iHiI55mvLx+UisFc+YsJSMjuL7EjSJJQQVWIa+H7g23jenqbar0shstzfhE +NoPBaB65o9c6qKCruajlcjZBxKuGlgP4wmIZMkFWwktHK5JJorLv9A29EpZp1fFs +VE6kSNQxebbIvgZCqNE2bOTMfGspKVxDENOXDNTNgsf/lArtQk1+PMwjW1zwFGGn +tiH3LbJtLgeeX7HnB80lsohD5d+6/v/xxrB6aWRlpQGT+271trZdR/pixh2dhsPg +92D0oyR6CQTwuE1CUIthIonN1m+PtMKnpodoVsRkMwMOshcMWzmWVNNNIy5BuEgK +Lhh30MdiiwlpLV5gkcnygd4hkMV2VO9j5sFllo5ttnkQ6OZKmNoAqcYWIHCW2sh8 +rDYFzwes5qhK/K+qYLbBroHli14/p6AZRAlkcS8Vc5ew40naHn1Dd7EKRA== +-----END CERTIFICATE----- diff --git a/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/resources/oauth2/certs/rootCA/rootCA.srl b/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/resources/oauth2/certs/rootCA/rootCA.srl new file mode 100644 index 00000000..3ddaad86 --- /dev/null +++ b/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/resources/oauth2/certs/rootCA/rootCA.srl @@ -0,0 +1 @@ +6C05509733CDE0014B58B238944F91CF0D5C1BE9 diff --git a/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/resources/oauth2/certs/solbroker_san.conf b/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/resources/oauth2/certs/solbroker_san.conf new file mode 100644 index 00000000..37bf92fb --- /dev/null +++ b/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/resources/oauth2/certs/solbroker_san.conf @@ -0,0 +1,26 @@ +[ req ] +req_extensions = req_ext +distinguished_name = req_distinguished_name +prompt = no + +[ req_distinguished_name ] +C = CA +ST = Ontario +L = Kanata +O = Solace Systems +OU = Solace Systems +CN = solbroker + +[ req_ext ] +subjectAltName = @alt_names + +[ alt_names ] +DNS.1 = solbroker +DNS.2 = solbroker1 +DNS.3 = solbroker_1 +DNS.4 = localhost +DNS.5 = solbroker2 +DNS.6 = solbroker_2 +IP.1 = 127.0.0.1 +IP.2 = 0:0:0:0:0:0:0:1 +# ... (add more SAN entries) \ No newline at end of file diff --git a/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/resources/oauth2/free-tier-broker-with-tls-and-oauth-docker-compose.yml b/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/resources/oauth2/free-tier-broker-with-tls-and-oauth-docker-compose.yml new file mode 100644 index 00000000..49a564a7 --- /dev/null +++ b/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/resources/oauth2/free-tier-broker-with-tls-and-oauth-docker-compose.yml @@ -0,0 +1,52 @@ +version: '3.5' + +networks: + solace_msg_net: + external: false + +services: + solbroker: + image: solace/solace-pubsub-standard:10.4.0.23 + hostname: solbroker + networks: + - solace_msg_net + env_file: + - ./solace_tls.env + shm_size: 2g + ulimits: + memlock: -1 + nofile: + soft: 2448 + hard: 42192 + secrets: + - server.pem + + solaceoauth: # A nginx reverse proxy for enabling SSL access to Keycloak + image: nginx:1.21.6 + hostname: solaceoauth + volumes: + - ./oauth/nginx.conf:/etc/nginx/nginx.conf + - ./oauth/www:/data/www + - ./certs/keycloak:/etc/sslcerts/ + networks: + - solace_msg_net + + keycloak: + image: quay.io/keycloak/keycloak:16.1.1 + hostname: keycloak + networks: + - solace_msg_net + volumes: + - ./oauth/keycloak/:/tmp/keycloak/ + environment: + - DB_VENDOR=h2 + - KEYCLOAK_USER=admin + - KEYCLOAK_PASSWORD=mysecret1! + - PROXY_ADDRESS_FORWARDING=true #important for reverse proxy + - "KEYCLOAK_IMPORT=/tmp/keycloak/solace-oauth-resource-server1-realm-export.json,/tmp/keycloak/solace-oauth-resource-server2-realm-export.json" + command: + - "-Dkeycloak.migration.strategy=IGNORE_EXISTING" + +secrets: + server.pem: + file: "certs/broker/solbroker.pem" ## The server certificate for the Solace PubSub+ broker diff --git a/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/resources/oauth2/oauth/README b/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/resources/oauth2/oauth/README new file mode 100644 index 00000000..ae851755 --- /dev/null +++ b/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/resources/oauth2/oauth/README @@ -0,0 +1,60 @@ +folder structure +---------------- + - ./keycloak: contains the realm export json files. + - ./certs/keycloak: contains the OAuth server private key and signed certificate + - ./www: contains the index.html file to be used for the nginx server health check + - ./nginx.conf: contains nginx reverse proxy configuration + + +sslcerts +-------- + - keycloak.key + - keycloak.crt + - keycloak.pem + + +nginx.conf +---------- + - SSL access to Keycloak can be enable either via a reverse proxy server or by configuration file changes directly on the Keycloak server container. + - SSL via external reverse proxy server like nginx is quite simile and a recommended way. + - Any traffic that comes to URL starting with https://localhost/auth is routed to the Keycloak container + - Any traffic that comes to http (port 80) will be redirected to https (port 443) as per the configuration + - nginx.conf file has additional comments where necessary + + +keycloak +--------- +The keycloak is configured with one realm, to be used as authorization server for Solace PubSub+ broker configured as resource server. + + - solace-oauth-resource-server-role-realm-export.json + As the name suggests this realm is for resource server role mode. + + - Keycloak request for fetching the well-known URLs (like userInfoUrl, tokenUrl, discoveryUrl etc) for given realm + curl --location --request GET 'https://localhost:10443/auth/realms/solace-oauth-client-role/.well-known/openid-configuration' + curl --location --request GET 'https://localhost:10443/auth/realms/solace-oauth-resource-server-role/.well-known/openid-configuration' + + - Keycloak request for fetching the Access and Id token + ``` + curl --location --request POST 'https://localhost:10443/auth/realms/solace-oauth-resource-server-role/protocol/openid-connect/token' \ + --header 'Content-Type: application/x-www-form-urlencoded' \ + --data-urlencode 'username=admin' \ + --data-urlencode 'password=mysecret!' \ + --data-urlencode 'client_id=solclient_oauth' \ + --data-urlencode 'grant_type=password' \ + --data-urlencode 'scope=openid audience-for-solace-oauth autz-group-for-solace-oauth subject-for-solace-oauth' \ + ``` + + The scope param contains the optional scope names. Depending on the required claims in the token we can add/remove scopes. + +High level steps in setting up the realm: + 1. Create a realm + 2. Optional, Change the realm access token timeout to 1 minutes + 3. Required, Create a Client with type = "confidential" and "service accounts enabled" + 4. Optional, Client > Scopes > remove all the existing scopes. So that unwanted claims are not included in token + 5. Required, Realm > Scopes > Create three custom scopes and corresponding mapper with hard coded value + - audience-for-solace-oauth - hardcoded claim value "solclient_oauth" (same as client name) for the "aud" claim in token + - autz-group-for-solace-oauth - hardcoded claim value "solclient_oauth_auth_group" for the "groups" claim in token + - subject-for-solace-oauth - hardcoded claim value "default" for the "sub" claim in token + 6. Required, Client > Scopes > Add the custom claims in the optional claims section + 7. Required, Realm > Users > Add a user named "admin" with password "mysecret!" + 8. Export a realm \ No newline at end of file diff --git a/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/resources/oauth2/oauth/keycloak/solace-oauth-resource-server1-realm-export.json b/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/resources/oauth2/oauth/keycloak/solace-oauth-resource-server1-realm-export.json new file mode 100644 index 00000000..d80d00ca --- /dev/null +++ b/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/resources/oauth2/oauth/keycloak/solace-oauth-resource-server1-realm-export.json @@ -0,0 +1,2276 @@ +{ + "id": "solace-oauth-resource-server1", + "realm": "solace-oauth-resource-server1", + "notBefore": 0, + "defaultSignatureAlgorithm": "RS256", + "revokeRefreshToken": false, + "refreshTokenMaxReuse": 0, + "accessTokenLifespan": 7200, + "accessTokenLifespanForImplicitFlow": 900, + "ssoSessionIdleTimeout": 1800, + "ssoSessionMaxLifespan": 36000, + "ssoSessionIdleTimeoutRememberMe": 0, + "ssoSessionMaxLifespanRememberMe": 0, + "offlineSessionIdleTimeout": 2592000, + "offlineSessionMaxLifespanEnabled": false, + "offlineSessionMaxLifespan": 5184000, + "clientSessionIdleTimeout": 0, + "clientSessionMaxLifespan": 0, + "clientOfflineSessionIdleTimeout": 0, + "clientOfflineSessionMaxLifespan": 0, + "accessCodeLifespan": 60, + "accessCodeLifespanUserAction": 300, + "accessCodeLifespanLogin": 1800, + "actionTokenGeneratedByAdminLifespan": 43200, + "actionTokenGeneratedByUserLifespan": 300, + "oauth2DeviceCodeLifespan": 600, + "oauth2DevicePollingInterval": 5, + "enabled": true, + "sslRequired": "external", + "registrationAllowed": false, + "registrationEmailAsUsername": false, + "rememberMe": false, + "verifyEmail": false, + "loginWithEmailAllowed": true, + "duplicateEmailsAllowed": false, + "resetPasswordAllowed": false, + "editUsernameAllowed": false, + "bruteForceProtected": false, + "permanentLockout": false, + "maxFailureWaitSeconds": 900, + "minimumQuickLoginWaitSeconds": 60, + "waitIncrementSeconds": 60, + "quickLoginCheckMilliSeconds": 1000, + "maxDeltaTimeSeconds": 43200, + "failureFactor": 30, + "roles": { + "realm": [ + { + "id": "bdda6bc5-75eb-449f-8521-a2faec50a08d", + "name": "default-roles-solace-oauth-resource-server1", + "description": "${role_default-roles}", + "composite": true, + "composites": { + "realm": [ + "offline_access", + "uma_authorization" + ], + "client": { + "account": [ + "manage-account", + "view-profile" + ] + } + }, + "clientRole": false, + "containerId": "solace-oauth-resource-server1", + "attributes": {} + }, + { + "id": "2e5b3f5c-6bb7-4f35-94c3-a277948e2f06", + "name": "uma_authorization", + "description": "${role_uma_authorization}", + "composite": false, + "clientRole": false, + "containerId": "solace-oauth-resource-server1", + "attributes": {} + }, + { + "id": "844b9405-65bf-4b12-8cd6-e88860d2afe1", + "name": "offline_access", + "description": "${role_offline-access}", + "composite": false, + "clientRole": false, + "containerId": "solace-oauth-resource-server1", + "attributes": {} + } + ], + "client": { + "realm-management": [ + { + "id": "85865318-e7e8-4511-ae1b-a09ff49bf283", + "name": "manage-identity-providers", + "description": "${role_manage-identity-providers}", + "composite": false, + "clientRole": true, + "containerId": "d541c266-38a9-4b28-b78d-b15d268bc612", + "attributes": {} + }, + { + "id": "b6fb2701-b3dd-44cb-bc86-cd47c7c225ef", + "name": "manage-authorization", + "description": "${role_manage-authorization}", + "composite": false, + "clientRole": true, + "containerId": "d541c266-38a9-4b28-b78d-b15d268bc612", + "attributes": {} + }, + { + "id": "925a03f4-8a0c-4959-9f37-72964654ea03", + "name": "manage-users", + "description": "${role_manage-users}", + "composite": false, + "clientRole": true, + "containerId": "d541c266-38a9-4b28-b78d-b15d268bc612", + "attributes": {} + }, + { + "id": "40402bdd-018c-4119-8cf6-72aa2cf5fb71", + "name": "view-identity-providers", + "description": "${role_view-identity-providers}", + "composite": false, + "clientRole": true, + "containerId": "d541c266-38a9-4b28-b78d-b15d268bc612", + "attributes": {} + }, + { + "id": "48ea6621-9819-4090-80fe-c0e44305210d", + "name": "query-groups", + "description": "${role_query-groups}", + "composite": false, + "clientRole": true, + "containerId": "d541c266-38a9-4b28-b78d-b15d268bc612", + "attributes": {} + }, + { + "id": "9abaa11c-4db2-4e51-ad84-ea1393333da5", + "name": "view-authorization", + "description": "${role_view-authorization}", + "composite": false, + "clientRole": true, + "containerId": "d541c266-38a9-4b28-b78d-b15d268bc612", + "attributes": {} + }, + { + "id": "5cd096c9-7032-45d6-8899-34a072abe109", + "name": "query-clients", + "description": "${role_query-clients}", + "composite": false, + "clientRole": true, + "containerId": "d541c266-38a9-4b28-b78d-b15d268bc612", + "attributes": {} + }, + { + "id": "2c609f41-70e2-4dc7-baa5-9275d4c08ae9", + "name": "view-clients", + "description": "${role_view-clients}", + "composite": true, + "composites": { + "client": { + "realm-management": [ + "query-clients" + ] + } + }, + "clientRole": true, + "containerId": "d541c266-38a9-4b28-b78d-b15d268bc612", + "attributes": {} + }, + { + "id": "d51078b9-0585-4f61-8ce9-3c73452b105b", + "name": "manage-realm", + "description": "${role_manage-realm}", + "composite": false, + "clientRole": true, + "containerId": "d541c266-38a9-4b28-b78d-b15d268bc612", + "attributes": {} + }, + { + "id": "553070f0-aca8-4448-9f25-fcbff5cd2dcc", + "name": "impersonation", + "description": "${role_impersonation}", + "composite": false, + "clientRole": true, + "containerId": "d541c266-38a9-4b28-b78d-b15d268bc612", + "attributes": {} + }, + { + "id": "1702c190-2ceb-4c2f-8356-ba39ebe09a64", + "name": "realm-admin", + "description": "${role_realm-admin}", + "composite": true, + "composites": { + "client": { + "realm-management": [ + "manage-identity-providers", + "manage-authorization", + "view-identity-providers", + "manage-users", + "query-groups", + "view-authorization", + "query-clients", + "view-clients", + "manage-realm", + "impersonation", + "manage-clients", + "create-client", + "view-events", + "manage-events", + "view-realm", + "view-users", + "query-users", + "query-realms" + ] + } + }, + "clientRole": true, + "containerId": "d541c266-38a9-4b28-b78d-b15d268bc612", + "attributes": {} + }, + { + "id": "312152f8-e339-456a-b020-20c17e78c2ab", + "name": "manage-clients", + "description": "${role_manage-clients}", + "composite": false, + "clientRole": true, + "containerId": "d541c266-38a9-4b28-b78d-b15d268bc612", + "attributes": {} + }, + { + "id": "7c5bd1cf-c5c0-4b62-aeb1-041b2290f1b1", + "name": "create-client", + "description": "${role_create-client}", + "composite": false, + "clientRole": true, + "containerId": "d541c266-38a9-4b28-b78d-b15d268bc612", + "attributes": {} + }, + { + "id": "4e1340fa-e318-44bc-b430-85310342d3dc", + "name": "view-events", + "description": "${role_view-events}", + "composite": false, + "clientRole": true, + "containerId": "d541c266-38a9-4b28-b78d-b15d268bc612", + "attributes": {} + }, + { + "id": "51bec016-19b0-456c-a0eb-12cd194b1188", + "name": "manage-events", + "description": "${role_manage-events}", + "composite": false, + "clientRole": true, + "containerId": "d541c266-38a9-4b28-b78d-b15d268bc612", + "attributes": {} + }, + { + "id": "52bb2127-ec4b-4cb7-9518-a995d35da31d", + "name": "view-realm", + "description": "${role_view-realm}", + "composite": false, + "clientRole": true, + "containerId": "d541c266-38a9-4b28-b78d-b15d268bc612", + "attributes": {} + }, + { + "id": "3caaba7c-0079-430c-9bba-419d35a69294", + "name": "view-users", + "description": "${role_view-users}", + "composite": true, + "composites": { + "client": { + "realm-management": [ + "query-groups", + "query-users" + ] + } + }, + "clientRole": true, + "containerId": "d541c266-38a9-4b28-b78d-b15d268bc612", + "attributes": {} + }, + { + "id": "ccfa8727-d590-48e3-af71-05672392d99a", + "name": "query-users", + "description": "${role_query-users}", + "composite": false, + "clientRole": true, + "containerId": "d541c266-38a9-4b28-b78d-b15d268bc612", + "attributes": {} + }, + { + "id": "d7c921d0-bb1e-422d-8f65-0c4dbafbf8c3", + "name": "query-realms", + "description": "${role_query-realms}", + "composite": false, + "clientRole": true, + "containerId": "d541c266-38a9-4b28-b78d-b15d268bc612", + "attributes": {} + } + ], + "security-admin-console": [], + "admin-cli": [], + "solclient_oauth": [], + "account-console": [], + "broker": [ + { + "id": "1e228e30-99a1-4edb-b6be-0c4134dabe91", + "name": "read-token", + "description": "${role_read-token}", + "composite": false, + "clientRole": true, + "containerId": "e7d16a15-00b6-4123-93ee-ef946e307c31", + "attributes": {} + } + ], + "account": [ + { + "id": "2b80753e-b5d9-4691-9917-cd583c1254e7", + "name": "manage-consent", + "description": "${role_manage-consent}", + "composite": true, + "composites": { + "client": { + "account": [ + "view-consent" + ] + } + }, + "clientRole": true, + "containerId": "c2042faf-a667-466d-bbb4-aecb63aa991f", + "attributes": {} + }, + { + "id": "2266aa22-2f3d-4a32-8378-fd18c3eeac6b", + "name": "view-applications", + "description": "${role_view-applications}", + "composite": false, + "clientRole": true, + "containerId": "c2042faf-a667-466d-bbb4-aecb63aa991f", + "attributes": {} + }, + { + "id": "29aa6bbf-a1ab-4600-925e-ae7d558841bd", + "name": "manage-account", + "description": "${role_manage-account}", + "composite": true, + "composites": { + "client": { + "account": [ + "manage-account-links" + ] + } + }, + "clientRole": true, + "containerId": "c2042faf-a667-466d-bbb4-aecb63aa991f", + "attributes": {} + }, + { + "id": "ae53ffdb-0e03-469b-b4d6-c3d6f1ae089f", + "name": "view-profile", + "description": "${role_view-profile}", + "composite": false, + "clientRole": true, + "containerId": "c2042faf-a667-466d-bbb4-aecb63aa991f", + "attributes": {} + }, + { + "id": "6fe55a48-5732-41f5-a658-c3f82dd2afcc", + "name": "delete-account", + "description": "${role_delete-account}", + "composite": false, + "clientRole": true, + "containerId": "c2042faf-a667-466d-bbb4-aecb63aa991f", + "attributes": {} + }, + { + "id": "de4f98ac-c1d1-4bed-bacf-e34d068ba7de", + "name": "view-consent", + "description": "${role_view-consent}", + "composite": false, + "clientRole": true, + "containerId": "c2042faf-a667-466d-bbb4-aecb63aa991f", + "attributes": {} + }, + { + "id": "083c1f91-5bb7-4c82-b6c0-3e72c5ae080a", + "name": "manage-account-links", + "description": "${role_manage-account-links}", + "composite": false, + "clientRole": true, + "containerId": "c2042faf-a667-466d-bbb4-aecb63aa991f", + "attributes": {} + } + ] + } + }, + "groups": [], + "defaultRole": { + "id": "bdda6bc5-75eb-449f-8521-a2faec50a08d", + "name": "default-roles-solace-oauth-resource-server1", + "description": "${role_default-roles}", + "composite": true, + "clientRole": false, + "containerId": "solace-oauth-resource-server1" + }, + "requiredCredentials": [ + "password" + ], + "otpPolicyType": "totp", + "otpPolicyAlgorithm": "HmacSHA1", + "otpPolicyInitialCounter": 0, + "otpPolicyDigits": 6, + "otpPolicyLookAheadWindow": 1, + "otpPolicyPeriod": 30, + "otpSupportedApplications": [ + "FreeOTP", + "Google Authenticator" + ], + "webAuthnPolicyRpEntityName": "keycloak", + "webAuthnPolicySignatureAlgorithms": [ + "ES256" + ], + "webAuthnPolicyRpId": "", + "webAuthnPolicyAttestationConveyancePreference": "not specified", + "webAuthnPolicyAuthenticatorAttachment": "not specified", + "webAuthnPolicyRequireResidentKey": "not specified", + "webAuthnPolicyUserVerificationRequirement": "not specified", + "webAuthnPolicyCreateTimeout": 0, + "webAuthnPolicyAvoidSameAuthenticatorRegister": false, + "webAuthnPolicyAcceptableAaguids": [], + "webAuthnPolicyPasswordlessRpEntityName": "keycloak", + "webAuthnPolicyPasswordlessSignatureAlgorithms": [ + "ES256" + ], + "webAuthnPolicyPasswordlessRpId": "", + "webAuthnPolicyPasswordlessAttestationConveyancePreference": "not specified", + "webAuthnPolicyPasswordlessAuthenticatorAttachment": "not specified", + "webAuthnPolicyPasswordlessRequireResidentKey": "not specified", + "webAuthnPolicyPasswordlessUserVerificationRequirement": "not specified", + "webAuthnPolicyPasswordlessCreateTimeout": 0, + "webAuthnPolicyPasswordlessAvoidSameAuthenticatorRegister": false, + "webAuthnPolicyPasswordlessAcceptableAaguids": [], + "users": [ + { + "username": "service-account-solclient_oauth", + "enabled": true, + "totp": false, + "emailVerified": false, + "serviceAccountClientId": "solclient_oauth", + "disableableCredentialTypes": [], + "requiredActions": [], + "realmRoles": [ + "default-roles-solace-oauth-resource-server1" + ], + "notBefore": 0, + "groups": [] + } + ], + "scopeMappings": [ + { + "clientScope": "offline_access", + "roles": [ + "offline_access" + ] + } + ], + "clientScopeMappings": { + "account": [ + { + "client": "account-console", + "roles": [ + "manage-account" + ] + } + ] + }, + "clients": [ + { + "id": "c2042faf-a667-466d-bbb4-aecb63aa991f", + "clientId": "account", + "name": "${client_account}", + "rootUrl": "${authBaseUrl}", + "baseUrl": "/realms/solace-oauth-resource-server1/account/", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "redirectUris": [ + "/realms/solace-oauth-resource-server1/account/*" + ], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "publicClient": true, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": {}, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": 0, + "defaultClientScopes": [ + "web-origins", + "profile", + "roles", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ] + }, + { + "id": "6849a52c-b27c-4727-851f-1211bf2292d0", + "clientId": "account-console", + "name": "${client_account-console}", + "rootUrl": "${authBaseUrl}", + "baseUrl": "/realms/solace-oauth-resource-server1/account/", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "redirectUris": [ + "/realms/solace-oauth-resource-server1/account/*" + ], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "publicClient": true, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": { + "pkce.code.challenge.method": "S256" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": 0, + "protocolMappers": [ + { + "id": "42bbbdaf-414a-4ca3-840a-cdf329f15fef", + "name": "audience resolve", + "protocol": "openid-connect", + "protocolMapper": "oidc-audience-resolve-mapper", + "consentRequired": false, + "config": {} + } + ], + "defaultClientScopes": [ + "web-origins", + "profile", + "roles", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ] + }, + { + "id": "c3e2dd5a-93df-4acd-b0ed-f91f06423882", + "clientId": "admin-cli", + "name": "${client_admin-cli}", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "redirectUris": [], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": false, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": true, + "serviceAccountsEnabled": false, + "publicClient": true, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": {}, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": 0, + "defaultClientScopes": [ + "web-origins", + "profile", + "roles", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ] + }, + { + "id": "e7d16a15-00b6-4123-93ee-ef946e307c31", + "clientId": "broker", + "name": "${client_broker}", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "redirectUris": [], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": true, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "publicClient": false, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": {}, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": 0, + "defaultClientScopes": [ + "web-origins", + "profile", + "roles", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ] + }, + { + "id": "d541c266-38a9-4b28-b78d-b15d268bc612", + "clientId": "realm-management", + "name": "${client_realm-management}", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "redirectUris": [], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": true, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "publicClient": false, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": {}, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": 0, + "defaultClientScopes": [ + "web-origins", + "profile", + "roles", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ] + }, + { + "id": "035b3a63-deb4-43a5-b890-295e70916e13", + "clientId": "security-admin-console", + "name": "${client_security-admin-console}", + "rootUrl": "${authAdminUrl}", + "baseUrl": "/admin/solace-oauth-resource-server1/console/", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "redirectUris": [ + "/admin/solace-oauth-resource-server1/console/*" + ], + "webOrigins": [ + "+" + ], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "publicClient": true, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": { + "pkce.code.challenge.method": "S256" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": 0, + "protocolMappers": [ + { + "id": "47daabc3-9e21-4d79-b2c8-6cf5b2e9b84f", + "name": "locale", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "locale", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "locale", + "jsonType.label": "String" + } + } + ], + "defaultClientScopes": [ + "web-origins", + "profile", + "roles", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ] + }, + { + "id": "ca72d62e-5a3f-43e1-8cfa-8b6e29e82415", + "clientId": "solclient_oauth", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "secret": "j6gWnw13iqzJfFZzlqzaQabQgXza4oHl", + "redirectUris": [ + "localhost:8080" + ], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": true, + "serviceAccountsEnabled": true, + "publicClient": false, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": { + "id.token.as.detached.signature": "false", + "saml.assertion.signature": "false", + "access.token.lifespan": "60", + "saml.force.post.binding": "false", + "saml.multivalued.roles": "false", + "saml.encrypt": "false", + "oauth2.device.authorization.grant.enabled": "false", + "backchannel.logout.revoke.offline.tokens": "false", + "saml.server.signature": "false", + "saml.server.signature.keyinfo.ext": "false", + "use.refresh.tokens": "true", + "exclude.session.state.from.auth.response": "false", + "oidc.ciba.grant.enabled": "false", + "saml.artifact.binding": "false", + "backchannel.logout.session.required": "true", + "client_credentials.use_refresh_token": "false", + "saml_force_name_id_format": "false", + "require.pushed.authorization.requests": "false", + "saml.client.signature": "false", + "tls.client.certificate.bound.access.tokens": "false", + "saml.authnstatement": "false", + "display.on.consent.screen": "false", + "saml.onetimeuse.condition": "false" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": true, + "nodeReRegistrationTimeout": -1, + "defaultClientScopes": [], + "optionalClientScopes": [ + "audience-for-solace-oauth", + "autz-group-for-solace-oauth", + "subject-for-solace-oauth" + ] + } + ], + "clientScopes": [ + { + "id": "d316150c-537b-4f09-b6b8-0f6b5e6d71b4", + "name": "audience-for-solace-oauth", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "display.on.consent.screen": "true" + }, + "protocolMappers": [ + { + "id": "2041d5d8-4a5d-46ca-b081-c0bf0a6a7952", + "name": "audience-claim-mapper", + "protocol": "openid-connect", + "protocolMapper": "oidc-audience-mapper", + "consentRequired": false, + "config": { + "included.client.audience": "solclient_oauth", + "id.token.claim": "true", + "access.token.claim": "true", + "userinfo.token.claim": "true" + } + } + ] + }, + { + "id": "ec8f6ef3-47d7-469f-b8cb-5401957e5b38", + "name": "offline_access", + "description": "OpenID Connect built-in scope: offline_access", + "protocol": "openid-connect", + "attributes": { + "consent.screen.text": "${offlineAccessScopeConsentText}", + "display.on.consent.screen": "true" + } + }, + { + "id": "a07707ab-769c-466c-ba6d-5976560a20ae", + "name": "microprofile-jwt", + "description": "Microprofile - JWT built-in scope", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "display.on.consent.screen": "false" + }, + "protocolMappers": [ + { + "id": "15477678-dc59-44e9-a031-5dd0f9eaf26e", + "name": "groups", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-realm-role-mapper", + "consentRequired": false, + "config": { + "multivalued": "true", + "userinfo.token.claim": "true", + "user.attribute": "foo", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "groups", + "jsonType.label": "String" + } + }, + { + "id": "3983df24-cbdf-4067-999c-f641f96e4aa8", + "name": "upn", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "username", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "upn", + "jsonType.label": "String" + } + } + ] + }, + { + "id": "26e7b8e5-b439-4bf3-9af2-d9bc69943402", + "name": "subject-for-solace-oauth", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "display.on.consent.screen": "true" + }, + "protocolMappers": [ + { + "id": "d16309b8-f991-4651-a72b-a974d3405aa2", + "name": "subject-for-solace-oauth-mapper", + "protocol": "openid-connect", + "protocolMapper": "oidc-hardcoded-claim-mapper", + "consentRequired": false, + "config": { + "claim.value": "default", + "userinfo.token.claim": "true", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "sub", + "jsonType.label": "String", + "access.tokenResponse.claim": "false" + } + } + ] + }, + { + "id": "b4fa6af1-f1f5-4e8e-897b-15a46a381c7c", + "name": "autz-group-for-solace-oauth", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "display.on.consent.screen": "true" + }, + "protocolMappers": [ + { + "id": "24ae8971-bfde-4b15-ab60-6d8490baf2e3", + "name": "authz-group-mapper", + "protocol": "openid-connect", + "protocolMapper": "oidc-hardcoded-claim-mapper", + "consentRequired": false, + "config": { + "claim.value": "solclient_oauth_auth_group", + "userinfo.token.claim": "true", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "groups", + "jsonType.label": "String", + "access.tokenResponse.claim": "false" + } + } + ] + }, + { + "id": "16320014-ba72-4e98-8f1a-8bfdebd8bd7b", + "name": "address", + "description": "OpenID Connect built-in scope: address", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "display.on.consent.screen": "true", + "consent.screen.text": "${addressScopeConsentText}" + }, + "protocolMappers": [ + { + "id": "d4f85fe2-588c-4c0f-a879-5333c625510d", + "name": "address", + "protocol": "openid-connect", + "protocolMapper": "oidc-address-mapper", + "consentRequired": false, + "config": { + "user.attribute.formatted": "formatted", + "user.attribute.country": "country", + "user.attribute.postal_code": "postal_code", + "userinfo.token.claim": "true", + "user.attribute.street": "street", + "id.token.claim": "true", + "user.attribute.region": "region", + "access.token.claim": "true", + "user.attribute.locality": "locality" + } + } + ] + }, + { + "id": "f3083007-6fbd-463c-9f92-b6c46a914c66", + "name": "profile", + "description": "OpenID Connect built-in scope: profile", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "display.on.consent.screen": "true", + "consent.screen.text": "${profileScopeConsentText}" + }, + "protocolMappers": [ + { + "id": "a55535ef-2342-45d5-b663-54e6bc3ff9b3", + "name": "website", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "website", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "website", + "jsonType.label": "String" + } + }, + { + "id": "2a9fcc4d-e06f-4196-a3c0-0a452fd33d64", + "name": "given name", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "firstName", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "given_name", + "jsonType.label": "String" + } + }, + { + "id": "0c5e7e16-a5b0-4b9f-8cb1-0a63a31ce5c6", + "name": "full name", + "protocol": "openid-connect", + "protocolMapper": "oidc-full-name-mapper", + "consentRequired": false, + "config": { + "id.token.claim": "true", + "access.token.claim": "true", + "userinfo.token.claim": "true" + } + }, + { + "id": "9cce90e3-1d71-4153-9d3a-f17d5a2f5296", + "name": "profile", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "profile", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "profile", + "jsonType.label": "String" + } + }, + { + "id": "8cc21024-28eb-4481-8fdd-4d7ac02874f7", + "name": "family name", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "lastName", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "family_name", + "jsonType.label": "String" + } + }, + { + "id": "f321a059-5ca4-444e-b32f-47533ced0139", + "name": "birthdate", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "birthdate", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "birthdate", + "jsonType.label": "String" + } + }, + { + "id": "d8d793e1-41ca-437f-9dfa-a3dfdb80159b", + "name": "gender", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "gender", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "gender", + "jsonType.label": "String" + } + }, + { + "id": "5b9a2378-488d-4470-bc73-e342b2db87d8", + "name": "updated at", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "updatedAt", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "updated_at", + "jsonType.label": "String" + } + }, + { + "id": "d69de674-2c5d-4347-8e0f-f019fadcfda8", + "name": "locale", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "locale", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "locale", + "jsonType.label": "String" + } + }, + { + "id": "ff32b8c6-863b-4167-80ed-5f5be5c17327", + "name": "username", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "username", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "preferred_username", + "jsonType.label": "String" + } + }, + { + "id": "3829eb41-e62f-4846-b0e4-3d35f3232ef8", + "name": "picture", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "picture", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "picture", + "jsonType.label": "String" + } + }, + { + "id": "d8d19c54-519e-4a6e-8687-e1ed2a09c6ad", + "name": "zoneinfo", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "zoneinfo", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "zoneinfo", + "jsonType.label": "String" + } + }, + { + "id": "33dc34fc-0585-4ae5-a8ce-5b74d54e88dc", + "name": "nickname", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "nickname", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "nickname", + "jsonType.label": "String" + } + }, + { + "id": "453902b9-deb7-475c-912b-159fea3b0032", + "name": "middle name", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "middleName", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "middle_name", + "jsonType.label": "String" + } + } + ] + }, + { + "id": "f1b39a65-489a-4a23-be2c-4b833effd930", + "name": "web-origins", + "description": "OpenID Connect scope for add allowed web origins to the access token", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "false", + "display.on.consent.screen": "false", + "consent.screen.text": "" + }, + "protocolMappers": [ + { + "id": "90b9d047-034a-4606-887c-fad91b70d054", + "name": "allowed web origins", + "protocol": "openid-connect", + "protocolMapper": "oidc-allowed-origins-mapper", + "consentRequired": false, + "config": {} + } + ] + }, + { + "id": "3c3c0a02-6f21-4df6-9b36-ec4ca22e7339", + "name": "role_list", + "description": "SAML role list", + "protocol": "saml", + "attributes": { + "consent.screen.text": "${samlRoleListScopeConsentText}", + "display.on.consent.screen": "true" + }, + "protocolMappers": [ + { + "id": "61bb315a-894b-4cf9-93dd-46674e34f032", + "name": "role list", + "protocol": "saml", + "protocolMapper": "saml-role-list-mapper", + "consentRequired": false, + "config": { + "single": "false", + "attribute.nameformat": "Basic", + "attribute.name": "Role" + } + } + ] + }, + { + "id": "c3f097ba-0d14-427e-8e2a-0f1de79e1936", + "name": "roles", + "description": "OpenID Connect scope for add user roles to the access token", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "false", + "display.on.consent.screen": "true", + "consent.screen.text": "${rolesScopeConsentText}" + }, + "protocolMappers": [ + { + "id": "24011624-b743-4693-b116-3faca08c400c", + "name": "audience resolve", + "protocol": "openid-connect", + "protocolMapper": "oidc-audience-resolve-mapper", + "consentRequired": false, + "config": {} + }, + { + "id": "a2f82fd6-a037-4a28-8278-1947bd2fd9f2", + "name": "client roles", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-client-role-mapper", + "consentRequired": false, + "config": { + "user.attribute": "foo", + "access.token.claim": "true", + "claim.name": "resource_access.${client_id}.roles", + "jsonType.label": "String", + "multivalued": "true" + } + }, + { + "id": "171d8632-91ee-4bdf-934b-f5b120db0237", + "name": "realm roles", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-realm-role-mapper", + "consentRequired": false, + "config": { + "user.attribute": "foo", + "access.token.claim": "true", + "claim.name": "realm_access.roles", + "jsonType.label": "String", + "multivalued": "true" + } + } + ] + }, + { + "id": "2b3ed8c0-59aa-462a-90e4-392c8caa2e14", + "name": "phone", + "description": "OpenID Connect built-in scope: phone", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "display.on.consent.screen": "true", + "consent.screen.text": "${phoneScopeConsentText}" + }, + "protocolMappers": [ + { + "id": "9f4263c8-9f0a-40dd-975b-9801fc9f7511", + "name": "phone number", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "phoneNumber", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "phone_number", + "jsonType.label": "String" + } + }, + { + "id": "f956ddd5-5ef6-405b-aa4c-b2c5fb857b80", + "name": "phone number verified", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "phoneNumberVerified", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "phone_number_verified", + "jsonType.label": "boolean" + } + } + ] + }, + { + "id": "3a696d48-9448-4721-a342-f9f6b1d96a5f", + "name": "email", + "description": "OpenID Connect built-in scope: email", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "display.on.consent.screen": "true", + "consent.screen.text": "${emailScopeConsentText}" + }, + "protocolMappers": [ + { + "id": "ef56a486-ed82-4156-b7db-d2b636383274", + "name": "email", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "email", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "email", + "jsonType.label": "String" + } + }, + { + "id": "fff7f5d7-2b27-4893-b0f5-ebfa31c1745f", + "name": "email verified", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "emailVerified", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "email_verified", + "jsonType.label": "boolean" + } + } + ] + } + ], + "defaultDefaultClientScopes": [ + "email", + "role_list", + "roles", + "web-origins", + "profile" + ], + "defaultOptionalClientScopes": [ + "address", + "phone", + "microprofile-jwt", + "offline_access" + ], + "browserSecurityHeaders": { + "contentSecurityPolicyReportOnly": "", + "xContentTypeOptions": "nosniff", + "xRobotsTag": "none", + "xFrameOptions": "SAMEORIGIN", + "contentSecurityPolicy": "frame-src 'self'; frame-ancestors 'self'; object-src 'none';", + "xXSSProtection": "1; mode=block", + "strictTransportSecurity": "max-age=31536000; includeSubDomains" + }, + "smtpServer": {}, + "eventsEnabled": false, + "eventsListeners": [ + "jboss-logging" + ], + "enabledEventTypes": [], + "adminEventsEnabled": false, + "adminEventsDetailsEnabled": false, + "identityProviders": [], + "identityProviderMappers": [], + "components": { + "org.keycloak.services.clientregistration.policy.ClientRegistrationPolicy": [ + { + "id": "dc9326b6-bf59-4c8d-97a3-4eacbf65156b", + "name": "Trusted Hosts", + "providerId": "trusted-hosts", + "subType": "anonymous", + "subComponents": {}, + "config": { + "host-sending-registration-request-must-match": [ + "true" + ], + "client-uris-must-match": [ + "true" + ] + } + }, + { + "id": "9b67f7d3-d6f1-4d3f-a845-37792210f75e", + "name": "Max Clients Limit", + "providerId": "max-clients", + "subType": "anonymous", + "subComponents": {}, + "config": { + "max-clients": [ + "200" + ] + } + }, + { + "id": "3e071626-fc03-49e8-aeea-1dbc9bda92ca", + "name": "Allowed Client Scopes", + "providerId": "allowed-client-templates", + "subType": "anonymous", + "subComponents": {}, + "config": { + "allow-default-scopes": [ + "true" + ] + } + }, + { + "id": "39167b16-a0d8-4871-a69e-e1146047333e", + "name": "Allowed Client Scopes", + "providerId": "allowed-client-templates", + "subType": "authenticated", + "subComponents": {}, + "config": { + "allow-default-scopes": [ + "true" + ] + } + }, + { + "id": "f6093860-56d2-47c6-bec5-f00597562493", + "name": "Consent Required", + "providerId": "consent-required", + "subType": "anonymous", + "subComponents": {}, + "config": {} + }, + { + "id": "ac7a7a58-2d71-48d4-8701-550b7f53998a", + "name": "Allowed Protocol Mapper Types", + "providerId": "allowed-protocol-mappers", + "subType": "authenticated", + "subComponents": {}, + "config": { + "allowed-protocol-mapper-types": [ + "oidc-full-name-mapper", + "saml-user-property-mapper", + "oidc-usermodel-attribute-mapper", + "saml-role-list-mapper", + "oidc-usermodel-property-mapper", + "saml-user-attribute-mapper", + "oidc-address-mapper", + "oidc-sha256-pairwise-sub-mapper" + ] + } + }, + { + "id": "9c39686f-ab2f-4b32-8376-186bba863297", + "name": "Full Scope Disabled", + "providerId": "scope", + "subType": "anonymous", + "subComponents": {}, + "config": {} + }, + { + "id": "5a183745-1acc-45a9-9a73-8599f5a572f0", + "name": "Allowed Protocol Mapper Types", + "providerId": "allowed-protocol-mappers", + "subType": "anonymous", + "subComponents": {}, + "config": { + "allowed-protocol-mapper-types": [ + "saml-user-attribute-mapper", + "oidc-full-name-mapper", + "oidc-usermodel-property-mapper", + "saml-user-property-mapper", + "oidc-usermodel-attribute-mapper", + "oidc-address-mapper", + "saml-role-list-mapper", + "oidc-sha256-pairwise-sub-mapper" + ] + } + } + ], + "org.keycloak.userprofile.UserProfileProvider": [ + { + "id": "97b8087e-cb24-487c-8466-c525a067da32", + "providerId": "declarative-user-profile", + "subComponents": {}, + "config": {} + } + ], + "org.keycloak.keys.KeyProvider": [ + { + "id": "abb12870-75b9-4d43-9509-b25eef3aca7b", + "name": "rsa-enc-generated", + "providerId": "rsa-enc-generated", + "subComponents": {}, + "config": { + "priority": [ + "100" + ], + "algorithm": [ + "RSA-OAEP" + ] + } + }, + { + "id": "4e480cfc-fc28-49e4-95a3-f022668898ce", + "name": "aes-generated", + "providerId": "aes-generated", + "subComponents": {}, + "config": { + "priority": [ + "100" + ] + } + }, + { + "id": "cfa31746-63ff-40e1-a03e-efdb0f0a1202", + "name": "hmac-generated", + "providerId": "hmac-generated", + "subComponents": {}, + "config": { + "priority": [ + "100" + ], + "algorithm": [ + "HS256" + ] + } + }, + { + "id": "d5f12100-bcaa-4d6c-95fc-81e6e20f8824", + "name": "rsa-generated", + "providerId": "rsa-generated", + "subComponents": {}, + "config": { + "priority": [ + "100" + ] + } + } + ] + }, + "internationalizationEnabled": false, + "supportedLocales": [], + "authenticationFlows": [ + { + "id": "0adddce7-f23b-4265-bb20-9981d93c0be8", + "alias": "Account verification options", + "description": "Method with which to verity the existing account", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "idp-email-verification", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticatorFlow": true, + "requirement": "ALTERNATIVE", + "priority": 20, + "flowAlias": "Verify Existing Account by Re-authentication", + "userSetupAllowed": false, + "autheticatorFlow": true + } + ] + }, + { + "id": "47e7f60a-9a41-4f75-bda0-e26e5bd0f50e", + "alias": "Authentication Options", + "description": "Authentication options.", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "basic-auth", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "basic-auth-otp", + "authenticatorFlow": false, + "requirement": "DISABLED", + "priority": 20, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "auth-spnego", + "authenticatorFlow": false, + "requirement": "DISABLED", + "priority": 30, + "userSetupAllowed": false, + "autheticatorFlow": false + } + ] + }, + { + "id": "254b5acc-e25a-4a48-a7b0-6ba477f01fb1", + "alias": "Browser - Conditional OTP", + "description": "Flow to determine if the OTP is required for the authentication", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "conditional-user-configured", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "auth-otp-form", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 20, + "userSetupAllowed": false, + "autheticatorFlow": false + } + ] + }, + { + "id": "663a65ce-1a4e-4d03-9af7-68685e937817", + "alias": "Direct Grant - Conditional OTP", + "description": "Flow to determine if the OTP is required for the authentication", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "conditional-user-configured", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "direct-grant-validate-otp", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 20, + "userSetupAllowed": false, + "autheticatorFlow": false + } + ] + }, + { + "id": "2720a078-f52e-45b4-8db7-ce6a69028cdc", + "alias": "First broker login - Conditional OTP", + "description": "Flow to determine if the OTP is required for the authentication", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "conditional-user-configured", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "auth-otp-form", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 20, + "userSetupAllowed": false, + "autheticatorFlow": false + } + ] + }, + { + "id": "9e9950c6-b9ba-48ef-93ba-e703aa96fb53", + "alias": "Handle Existing Account", + "description": "Handle what to do if there is existing account with same email/username like authenticated identity provider", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "idp-confirm-link", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticatorFlow": true, + "requirement": "REQUIRED", + "priority": 20, + "flowAlias": "Account verification options", + "userSetupAllowed": false, + "autheticatorFlow": true + } + ] + }, + { + "id": "87cdbd1d-ca15-4503-9f96-56672b3373ab", + "alias": "Reset - Conditional OTP", + "description": "Flow to determine if the OTP should be reset or not. Set to REQUIRED to force.", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "conditional-user-configured", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "reset-otp", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 20, + "userSetupAllowed": false, + "autheticatorFlow": false + } + ] + }, + { + "id": "3cec4211-f9bd-40ac-bee9-a217c5bd4544", + "alias": "User creation or linking", + "description": "Flow for the existing/non-existing user alternatives", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticatorConfig": "create unique user config", + "authenticator": "idp-create-user-if-unique", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticatorFlow": true, + "requirement": "ALTERNATIVE", + "priority": 20, + "flowAlias": "Handle Existing Account", + "userSetupAllowed": false, + "autheticatorFlow": true + } + ] + }, + { + "id": "e81c01c4-71f1-4fc8-b389-63492ba66264", + "alias": "Verify Existing Account by Re-authentication", + "description": "Reauthentication of existing account", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "idp-username-password-form", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticatorFlow": true, + "requirement": "CONDITIONAL", + "priority": 20, + "flowAlias": "First broker login - Conditional OTP", + "userSetupAllowed": false, + "autheticatorFlow": true + } + ] + }, + { + "id": "2738f462-fa7d-474b-8247-caa1a414d3ba", + "alias": "browser", + "description": "browser based authentication", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "auth-cookie", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "auth-spnego", + "authenticatorFlow": false, + "requirement": "DISABLED", + "priority": 20, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "identity-provider-redirector", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 25, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticatorFlow": true, + "requirement": "ALTERNATIVE", + "priority": 30, + "flowAlias": "forms", + "userSetupAllowed": false, + "autheticatorFlow": true + } + ] + }, + { + "id": "bdd1acf7-7393-40e1-9028-35ee54045b08", + "alias": "clients", + "description": "Base authentication for clients", + "providerId": "client-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "client-secret", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "client-jwt", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 20, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "client-secret-jwt", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 30, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "client-x509", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 40, + "userSetupAllowed": false, + "autheticatorFlow": false + } + ] + }, + { + "id": "3f28ee1e-fef9-49f4-8dd9-56002c0c2da9", + "alias": "direct grant", + "description": "OpenID Connect Resource Owner Grant", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "direct-grant-validate-username", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "direct-grant-validate-password", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 20, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticatorFlow": true, + "requirement": "CONDITIONAL", + "priority": 30, + "flowAlias": "Direct Grant - Conditional OTP", + "userSetupAllowed": false, + "autheticatorFlow": true + } + ] + }, + { + "id": "931260e1-f965-45a9-8afc-31cd30ed49cb", + "alias": "docker auth", + "description": "Used by Docker clients to authenticate against the IDP", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "docker-http-basic-authenticator", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + } + ] + }, + { + "id": "d76e28f6-aa7b-4f57-b79c-a1abde10af23", + "alias": "first broker login", + "description": "Actions taken after first broker login with identity provider account, which is not yet linked to any Keycloak account", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticatorConfig": "review profile config", + "authenticator": "idp-review-profile", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticatorFlow": true, + "requirement": "REQUIRED", + "priority": 20, + "flowAlias": "User creation or linking", + "userSetupAllowed": false, + "autheticatorFlow": true + } + ] + }, + { + "id": "22497645-eaa9-40fa-8650-4558ec6636e9", + "alias": "forms", + "description": "Username, password, otp and other auth forms.", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "auth-username-password-form", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticatorFlow": true, + "requirement": "CONDITIONAL", + "priority": 20, + "flowAlias": "Browser - Conditional OTP", + "userSetupAllowed": false, + "autheticatorFlow": true + } + ] + }, + { + "id": "6030ff30-48ca-4e0d-8cf8-d142cbf159ed", + "alias": "http challenge", + "description": "An authentication flow based on challenge-response HTTP Authentication Schemes", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "no-cookie-redirect", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticatorFlow": true, + "requirement": "REQUIRED", + "priority": 20, + "flowAlias": "Authentication Options", + "userSetupAllowed": false, + "autheticatorFlow": true + } + ] + }, + { + "id": "8de1c97d-abc6-45ad-9705-b5b4701661e8", + "alias": "registration", + "description": "registration flow", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "registration-page-form", + "authenticatorFlow": true, + "requirement": "REQUIRED", + "priority": 10, + "flowAlias": "registration form", + "userSetupAllowed": false, + "autheticatorFlow": true + } + ] + }, + { + "id": "c56f2918-505e-463f-982f-caa67cdb5303", + "alias": "registration form", + "description": "registration form", + "providerId": "form-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "registration-user-creation", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 20, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "registration-profile-action", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 40, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "registration-password-action", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 50, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "registration-recaptcha-action", + "authenticatorFlow": false, + "requirement": "DISABLED", + "priority": 60, + "userSetupAllowed": false, + "autheticatorFlow": false + } + ] + }, + { + "id": "c5247bf0-faf4-4fda-bd03-c3beae3d37d2", + "alias": "reset credentials", + "description": "Reset credentials for a user if they forgot their password or something", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "reset-credentials-choose-user", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "reset-credential-email", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 20, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "reset-password", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 30, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticatorFlow": true, + "requirement": "CONDITIONAL", + "priority": 40, + "flowAlias": "Reset - Conditional OTP", + "userSetupAllowed": false, + "autheticatorFlow": true + } + ] + }, + { + "id": "07490c41-8a3a-49c6-98e8-7ed31a7a1fcd", + "alias": "saml ecp", + "description": "SAML ECP Profile Authentication Flow", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "http-basic-authenticator", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + } + ] + } + ], + "authenticatorConfig": [ + { + "id": "010507ce-ea67-4a21-8792-6eb4328649da", + "alias": "create unique user config", + "config": { + "require.password.update.after.registration": "false" + } + }, + { + "id": "27ab5f93-8b3e-4698-9358-2f20ee64eae1", + "alias": "review profile config", + "config": { + "update.profile.on.first.login": "missing" + } + } + ], + "requiredActions": [ + { + "alias": "CONFIGURE_TOTP", + "name": "Configure OTP", + "providerId": "CONFIGURE_TOTP", + "enabled": true, + "defaultAction": false, + "priority": 10, + "config": {} + }, + { + "alias": "terms_and_conditions", + "name": "Terms and Conditions", + "providerId": "terms_and_conditions", + "enabled": false, + "defaultAction": false, + "priority": 20, + "config": {} + }, + { + "alias": "UPDATE_PASSWORD", + "name": "Update Password", + "providerId": "UPDATE_PASSWORD", + "enabled": true, + "defaultAction": false, + "priority": 30, + "config": {} + }, + { + "alias": "UPDATE_PROFILE", + "name": "Update Profile", + "providerId": "UPDATE_PROFILE", + "enabled": true, + "defaultAction": false, + "priority": 40, + "config": {} + }, + { + "alias": "VERIFY_EMAIL", + "name": "Verify Email", + "providerId": "VERIFY_EMAIL", + "enabled": true, + "defaultAction": false, + "priority": 50, + "config": {} + }, + { + "alias": "delete_account", + "name": "Delete Account", + "providerId": "delete_account", + "enabled": false, + "defaultAction": false, + "priority": 60, + "config": {} + }, + { + "alias": "update_user_locale", + "name": "Update User Locale", + "providerId": "update_user_locale", + "enabled": true, + "defaultAction": false, + "priority": 1000, + "config": {} + } + ], + "browserFlow": "browser", + "registrationFlow": "registration", + "directGrantFlow": "direct grant", + "resetCredentialsFlow": "reset credentials", + "clientAuthenticationFlow": "clients", + "dockerAuthenticationFlow": "docker auth", + "attributes": { + "cibaBackchannelTokenDeliveryMode": "poll", + "cibaExpiresIn": "120", + "cibaAuthRequestedUserHint": "login_hint", + "oauth2DeviceCodeLifespan": "600", + "clientOfflineSessionMaxLifespan": "0", + "oauth2DevicePollingInterval": "5", + "clientSessionIdleTimeout": "0", + "parRequestUriLifespan": "60", + "clientSessionMaxLifespan": "0", + "frontendUrl": "https://solaceoauth:10443/auth/", + "clientOfflineSessionIdleTimeout": "0", + "cibaInterval": "5" + }, + "keycloakVersion": "16.1.1", + "userManagedAccessAllowed": false, + "clientProfiles": { + "profiles": [] + }, + "clientPolicies": { + "policies": [] + }, + "users": [ + { + "username": "admin", + "enabled": true, + "credentials": [ + { + "type": "mysecret!", + "value": "mysecret!" + } + ], + "clientRoles": { + "realm-management": [ "realm-admin" ], + "account": [ "manage-account" ] + } + } + ] +} \ No newline at end of file diff --git a/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/resources/oauth2/oauth/keycloak/solace-oauth-resource-server2-realm-export.json b/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/resources/oauth2/oauth/keycloak/solace-oauth-resource-server2-realm-export.json new file mode 100644 index 00000000..1cf7c14a --- /dev/null +++ b/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/resources/oauth2/oauth/keycloak/solace-oauth-resource-server2-realm-export.json @@ -0,0 +1,2136 @@ +{ + "realm": "solace-oauth-resource-server2", + "notBefore": 0, + "defaultSignatureAlgorithm": "RS256", + "revokeRefreshToken": false, + "refreshTokenMaxReuse": 0, + "accessTokenLifespan": 7200, + "accessTokenLifespanForImplicitFlow": 900, + "ssoSessionIdleTimeout": 1800, + "ssoSessionMaxLifespan": 36000, + "ssoSessionIdleTimeoutRememberMe": 0, + "ssoSessionMaxLifespanRememberMe": 0, + "offlineSessionIdleTimeout": 2592000, + "offlineSessionMaxLifespanEnabled": false, + "offlineSessionMaxLifespan": 5184000, + "clientSessionIdleTimeout": 0, + "clientSessionMaxLifespan": 0, + "clientOfflineSessionIdleTimeout": 0, + "clientOfflineSessionMaxLifespan": 0, + "accessCodeLifespan": 60, + "accessCodeLifespanUserAction": 300, + "accessCodeLifespanLogin": 1800, + "actionTokenGeneratedByAdminLifespan": 43200, + "actionTokenGeneratedByUserLifespan": 300, + "oauth2DeviceCodeLifespan": 600, + "oauth2DevicePollingInterval": 5, + "enabled": true, + "sslRequired": "external", + "registrationAllowed": false, + "registrationEmailAsUsername": false, + "rememberMe": false, + "verifyEmail": false, + "loginWithEmailAllowed": true, + "duplicateEmailsAllowed": false, + "resetPasswordAllowed": false, + "editUsernameAllowed": false, + "bruteForceProtected": false, + "permanentLockout": false, + "maxFailureWaitSeconds": 900, + "minimumQuickLoginWaitSeconds": 60, + "waitIncrementSeconds": 60, + "quickLoginCheckMilliSeconds": 1000, + "maxDeltaTimeSeconds": 43200, + "failureFactor": 30, + "roles": { + "realm": [ + { + "name": "default-roles-solace-oauth-resource-server2", + "description": "${role_default-roles}", + "composite": true, + "composites": { + "realm": [ + "offline_access", + "uma_authorization" + ], + "client": { + "account": [ + "manage-account", + "view-profile" + ] + } + }, + "clientRole": false, + "containerId": "solace-oauth-resource-server2", + "attributes": {} + }, + { + "name": "uma_authorization", + "description": "${role_uma_authorization}", + "composite": false, + "clientRole": false, + "containerId": "solace-oauth-resource-server2", + "attributes": {} + }, + { + "name": "offline_access", + "description": "${role_offline-access}", + "composite": false, + "clientRole": false, + "containerId": "solace-oauth-resource-server2", + "attributes": {} + } + ], + "client": { + "realm-management": [ + { + "name": "manage-identity-providers", + "description": "${role_manage-identity-providers}", + "composite": false, + "clientRole": true, + "attributes": {} + }, + { + "name": "manage-authorization", + "description": "${role_manage-authorization}", + "composite": false, + "clientRole": true, + "attributes": {} + }, + { + "name": "manage-users", + "description": "${role_manage-users}", + "composite": false, + "clientRole": true, + "attributes": {} + }, + { + "name": "view-identity-providers", + "description": "${role_view-identity-providers}", + "composite": false, + "clientRole": true, + "attributes": {} + }, + { + "name": "query-groups", + "description": "${role_query-groups}", + "composite": false, + "clientRole": true, + "attributes": {} + }, + { + "name": "view-authorization", + "description": "${role_view-authorization}", + "composite": false, + "clientRole": true, + "attributes": {} + }, + { + "name": "query-clients", + "description": "${role_query-clients}", + "composite": false, + "clientRole": true, + "attributes": {} + }, + { + "name": "view-clients", + "description": "${role_view-clients}", + "composite": true, + "composites": { + "client": { + "realm-management": [ + "query-clients" + ] + } + }, + "clientRole": true, + "attributes": {} + }, + { + "name": "manage-realm", + "description": "${role_manage-realm}", + "composite": false, + "clientRole": true, + "attributes": {} + }, + { + "name": "impersonation", + "description": "${role_impersonation}", + "composite": false, + "clientRole": true, + "attributes": {} + }, + { + "name": "realm-admin", + "description": "${role_realm-admin}", + "composite": true, + "composites": { + "client": { + "realm-management": [ + "manage-identity-providers", + "manage-authorization", + "view-identity-providers", + "manage-users", + "query-groups", + "view-authorization", + "query-clients", + "view-clients", + "manage-realm", + "impersonation", + "manage-clients", + "create-client", + "view-events", + "manage-events", + "view-realm", + "view-users", + "query-users", + "query-realms" + ] + } + }, + "clientRole": true, + "attributes": {} + }, + { + "name": "manage-clients", + "description": "${role_manage-clients}", + "composite": false, + "clientRole": true, + "attributes": {} + }, + { + "name": "create-client", + "description": "${role_create-client}", + "composite": false, + "clientRole": true, + "attributes": {} + }, + { + "name": "view-events", + "description": "${role_view-events}", + "composite": false, + "clientRole": true, + "attributes": {} + }, + { + "name": "manage-events", + "description": "${role_manage-events}", + "composite": false, + "clientRole": true, + "attributes": {} + }, + { + "name": "view-realm", + "description": "${role_view-realm}", + "composite": false, + "clientRole": true, + "attributes": {} + }, + { + "name": "view-users", + "description": "${role_view-users}", + "composite": true, + "composites": { + "client": { + "realm-management": [ + "query-groups", + "query-users" + ] + } + }, + "clientRole": true, + "attributes": {} + }, + { + "name": "query-users", + "description": "${role_query-users}", + "composite": false, + "clientRole": true, + "attributes": {} + }, + { + "name": "query-realms", + "description": "${role_query-realms}", + "composite": false, + "clientRole": true, + "attributes": {} + } + ], + "security-admin-console": [], + "admin-cli": [], + "solclient_oauth": [], + "account-console": [], + "broker": [ + { + "name": "read-token", + "description": "${role_read-token}", + "composite": false, + "clientRole": true, + "attributes": {} + } + ], + "account": [ + { + "name": "manage-consent", + "description": "${role_manage-consent}", + "composite": true, + "composites": { + "client": { + "account": [ + "view-consent" + ] + } + }, + "clientRole": true, + "attributes": {} + }, + { + "name": "view-applications", + "description": "${role_view-applications}", + "composite": false, + "clientRole": true, + "attributes": {} + }, + { + "name": "manage-account", + "description": "${role_manage-account}", + "composite": true, + "composites": { + "client": { + "account": [ + "manage-account-links" + ] + } + }, + "clientRole": true, + "attributes": {} + }, + { + "name": "view-profile", + "description": "${role_view-profile}", + "composite": false, + "clientRole": true, + "attributes": {} + }, + { + "name": "delete-account", + "description": "${role_delete-account}", + "composite": false, + "clientRole": true, + "attributes": {} + }, + { + "name": "view-consent", + "description": "${role_view-consent}", + "composite": false, + "clientRole": true, + "attributes": {} + }, + { + "name": "manage-account-links", + "description": "${role_manage-account-links}", + "composite": false, + "clientRole": true, + "attributes": {} + } + ] + } + }, + "groups": [], + "defaultRole": { + "name": "default-roles-solace-oauth-resource-server2", + "description": "${role_default-roles}", + "composite": true, + "clientRole": false, + "containerId": "solace-oauth-resource-server2" + }, + "requiredCredentials": [ + "password" + ], + "otpPolicyType": "totp", + "otpPolicyAlgorithm": "HmacSHA1", + "otpPolicyInitialCounter": 0, + "otpPolicyDigits": 6, + "otpPolicyLookAheadWindow": 1, + "otpPolicyPeriod": 30, + "otpSupportedApplications": [ + "FreeOTP", + "Google Authenticator" + ], + "webAuthnPolicyRpEntityName": "keycloak", + "webAuthnPolicySignatureAlgorithms": [ + "ES256" + ], + "webAuthnPolicyRpId": "", + "webAuthnPolicyAttestationConveyancePreference": "not specified", + "webAuthnPolicyAuthenticatorAttachment": "not specified", + "webAuthnPolicyRequireResidentKey": "not specified", + "webAuthnPolicyUserVerificationRequirement": "not specified", + "webAuthnPolicyCreateTimeout": 0, + "webAuthnPolicyAvoidSameAuthenticatorRegister": false, + "webAuthnPolicyAcceptableAaguids": [], + "webAuthnPolicyPasswordlessRpEntityName": "keycloak", + "webAuthnPolicyPasswordlessSignatureAlgorithms": [ + "ES256" + ], + "webAuthnPolicyPasswordlessRpId": "", + "webAuthnPolicyPasswordlessAttestationConveyancePreference": "not specified", + "webAuthnPolicyPasswordlessAuthenticatorAttachment": "not specified", + "webAuthnPolicyPasswordlessRequireResidentKey": "not specified", + "webAuthnPolicyPasswordlessUserVerificationRequirement": "not specified", + "webAuthnPolicyPasswordlessCreateTimeout": 0, + "webAuthnPolicyPasswordlessAvoidSameAuthenticatorRegister": false, + "webAuthnPolicyPasswordlessAcceptableAaguids": [], + "users": [ + { + "username": "service-account-solclient_oauth", + "enabled": true, + "totp": false, + "emailVerified": false, + "serviceAccountClientId": "solclient_oauth", + "disableableCredentialTypes": [], + "requiredActions": [], + "realmRoles": [ + "default-roles-solace-oauth-resource-server2" + ], + "notBefore": 0, + "groups": [] + } + ], + "scopeMappings": [ + { + "clientScope": "offline_access", + "roles": [ + "offline_access" + ] + } + ], + "clientScopeMappings": { + "account": [ + { + "client": "account-console", + "roles": [ + "manage-account" + ] + } + ] + }, + "clients": [ + { + "clientId": "account", + "name": "${client_account}", + "rootUrl": "${authBaseUrl}", + "baseUrl": "/realms/solace-oauth-resource-server2/account/", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "redirectUris": [ + "/realms/solace-oauth-resource-server2/account/*" + ], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "publicClient": true, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": {}, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": 0, + "defaultClientScopes": [ + "web-origins", + "profile", + "roles", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ] + }, + { + "clientId": "account-console", + "name": "${client_account-console}", + "rootUrl": "${authBaseUrl}", + "baseUrl": "/realms/solace-oauth-resource-server2/account/", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "redirectUris": [ + "/realms/solace-oauth-resource-server2/account/*" + ], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "publicClient": true, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": { + "pkce.code.challenge.method": "S256" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": 0, + "protocolMappers": [ + { + "name": "audience resolve", + "protocol": "openid-connect", + "protocolMapper": "oidc-audience-resolve-mapper", + "consentRequired": false, + "config": {} + } + ], + "defaultClientScopes": [ + "web-origins", + "profile", + "roles", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ] + }, + { + "clientId": "admin-cli", + "name": "${client_admin-cli}", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "redirectUris": [], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": false, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": true, + "serviceAccountsEnabled": false, + "publicClient": true, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": {}, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": 0, + "defaultClientScopes": [ + "web-origins", + "profile", + "roles", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ] + }, + { + "clientId": "broker", + "name": "${client_broker}", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "redirectUris": [], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": true, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "publicClient": false, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": {}, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": 0, + "defaultClientScopes": [ + "web-origins", + "profile", + "roles", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ] + }, + { + "clientId": "realm-management", + "name": "${client_realm-management}", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "redirectUris": [], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": true, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "publicClient": false, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": {}, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": 0, + "defaultClientScopes": [ + "web-origins", + "profile", + "roles", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ] + }, + { + "clientId": "security-admin-console", + "name": "${client_security-admin-console}", + "rootUrl": "${authAdminUrl}", + "baseUrl": "/admin/solace-oauth-resource-server2/console/", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "redirectUris": [ + "/admin/solace-oauth-resource-server2/console/*" + ], + "webOrigins": [ + "+" + ], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "publicClient": true, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": { + "pkce.code.challenge.method": "S256" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": 0, + "protocolMappers": [ + { + "name": "locale", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "locale", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "locale", + "jsonType.label": "String" + } + } + ], + "defaultClientScopes": [ + "web-origins", + "profile", + "roles", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ] + }, + { + "clientId": "solclient_oauth", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "secret": "j6gWnw13iqzJfFZzlqzaQabQgXza4oHl", + "redirectUris": [ + "localhost:8080" + ], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": true, + "serviceAccountsEnabled": true, + "publicClient": false, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": { + "id.token.as.detached.signature": "false", + "saml.assertion.signature": "false", + "access.token.lifespan": "60", + "saml.force.post.binding": "false", + "saml.multivalued.roles": "false", + "saml.encrypt": "false", + "oauth2.device.authorization.grant.enabled": "false", + "backchannel.logout.revoke.offline.tokens": "false", + "saml.server.signature": "false", + "saml.server.signature.keyinfo.ext": "false", + "use.refresh.tokens": "true", + "exclude.session.state.from.auth.response": "false", + "oidc.ciba.grant.enabled": "false", + "saml.artifact.binding": "false", + "backchannel.logout.session.required": "true", + "client_credentials.use_refresh_token": "false", + "saml_force_name_id_format": "false", + "require.pushed.authorization.requests": "false", + "saml.client.signature": "false", + "tls.client.certificate.bound.access.tokens": "false", + "saml.authnstatement": "false", + "display.on.consent.screen": "false", + "saml.onetimeuse.condition": "false" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": true, + "nodeReRegistrationTimeout": -1, + "defaultClientScopes": [], + "optionalClientScopes": [ + "audience-for-solace-oauth", + "autz-group-for-solace-oauth", + "subject-for-solace-oauth" + ] + } + ], + "clientScopes": [ + { + "name": "audience-for-solace-oauth", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "display.on.consent.screen": "true" + }, + "protocolMappers": [ + { + "name": "audience-claim-mapper", + "protocol": "openid-connect", + "protocolMapper": "oidc-audience-mapper", + "consentRequired": false, + "config": { + "included.client.audience": "solclient_oauth", + "id.token.claim": "true", + "access.token.claim": "true", + "userinfo.token.claim": "true" + } + } + ] + }, + { + "name": "offline_access", + "description": "OpenID Connect built-in scope: offline_access", + "protocol": "openid-connect", + "attributes": { + "consent.screen.text": "${offlineAccessScopeConsentText}", + "display.on.consent.screen": "true" + } + }, + { + "name": "microprofile-jwt", + "description": "Microprofile - JWT built-in scope", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "display.on.consent.screen": "false" + }, + "protocolMappers": [ + { + "name": "groups", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-realm-role-mapper", + "consentRequired": false, + "config": { + "multivalued": "true", + "userinfo.token.claim": "true", + "user.attribute": "foo", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "groups", + "jsonType.label": "String" + } + }, + { + "name": "upn", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "username", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "upn", + "jsonType.label": "String" + } + } + ] + }, + { + "name": "subject-for-solace-oauth", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "display.on.consent.screen": "true" + }, + "protocolMappers": [ + { + "name": "subject-for-solace-oauth-mapper", + "protocol": "openid-connect", + "protocolMapper": "oidc-hardcoded-claim-mapper", + "consentRequired": false, + "config": { + "claim.value": "default", + "userinfo.token.claim": "true", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "sub", + "jsonType.label": "String", + "access.tokenResponse.claim": "false" + } + } + ] + }, + { + "name": "autz-group-for-solace-oauth", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "display.on.consent.screen": "true" + }, + "protocolMappers": [ + { + "name": "authz-group-mapper", + "protocol": "openid-connect", + "protocolMapper": "oidc-hardcoded-claim-mapper", + "consentRequired": false, + "config": { + "claim.value": "solclient_oauth_auth_group", + "userinfo.token.claim": "true", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "groups", + "jsonType.label": "String", + "access.tokenResponse.claim": "false" + } + } + ] + }, + { + "name": "address", + "description": "OpenID Connect built-in scope: address", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "display.on.consent.screen": "true", + "consent.screen.text": "${addressScopeConsentText}" + }, + "protocolMappers": [ + { + "name": "address", + "protocol": "openid-connect", + "protocolMapper": "oidc-address-mapper", + "consentRequired": false, + "config": { + "user.attribute.formatted": "formatted", + "user.attribute.country": "country", + "user.attribute.postal_code": "postal_code", + "userinfo.token.claim": "true", + "user.attribute.street": "street", + "id.token.claim": "true", + "user.attribute.region": "region", + "access.token.claim": "true", + "user.attribute.locality": "locality" + } + } + ] + }, + { + "name": "profile", + "description": "OpenID Connect built-in scope: profile", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "display.on.consent.screen": "true", + "consent.screen.text": "${profileScopeConsentText}" + }, + "protocolMappers": [ + { + "name": "website", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "website", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "website", + "jsonType.label": "String" + } + }, + { + "name": "given name", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "firstName", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "given_name", + "jsonType.label": "String" + } + }, + { + "name": "full name", + "protocol": "openid-connect", + "protocolMapper": "oidc-full-name-mapper", + "consentRequired": false, + "config": { + "id.token.claim": "true", + "access.token.claim": "true", + "userinfo.token.claim": "true" + } + }, + { + "name": "profile", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "profile", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "profile", + "jsonType.label": "String" + } + }, + { + "name": "family name", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "lastName", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "family_name", + "jsonType.label": "String" + } + }, + { + "name": "birthdate", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "birthdate", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "birthdate", + "jsonType.label": "String" + } + }, + { + "name": "gender", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "gender", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "gender", + "jsonType.label": "String" + } + }, + { + "name": "updated at", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "updatedAt", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "updated_at", + "jsonType.label": "String" + } + }, + { + "name": "locale", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "locale", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "locale", + "jsonType.label": "String" + } + }, + { + "name": "username", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "username", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "preferred_username", + "jsonType.label": "String" + } + }, + { + "name": "picture", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "picture", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "picture", + "jsonType.label": "String" + } + }, + { + "name": "zoneinfo", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "zoneinfo", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "zoneinfo", + "jsonType.label": "String" + } + }, + { + "name": "nickname", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "nickname", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "nickname", + "jsonType.label": "String" + } + }, + { + "name": "middle name", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "middleName", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "middle_name", + "jsonType.label": "String" + } + } + ] + }, + { + "name": "web-origins", + "description": "OpenID Connect scope for add allowed web origins to the access token", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "false", + "display.on.consent.screen": "false", + "consent.screen.text": "" + }, + "protocolMappers": [ + { + "name": "allowed web origins", + "protocol": "openid-connect", + "protocolMapper": "oidc-allowed-origins-mapper", + "consentRequired": false, + "config": {} + } + ] + }, + { + "name": "role_list", + "description": "SAML role list", + "protocol": "saml", + "attributes": { + "consent.screen.text": "${samlRoleListScopeConsentText}", + "display.on.consent.screen": "true" + }, + "protocolMappers": [ + { + "name": "role list", + "protocol": "saml", + "protocolMapper": "saml-role-list-mapper", + "consentRequired": false, + "config": { + "single": "false", + "attribute.nameformat": "Basic", + "attribute.name": "Role" + } + } + ] + }, + { + "name": "roles", + "description": "OpenID Connect scope for add user roles to the access token", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "false", + "display.on.consent.screen": "true", + "consent.screen.text": "${rolesScopeConsentText}" + }, + "protocolMappers": [ + { + "name": "audience resolve", + "protocol": "openid-connect", + "protocolMapper": "oidc-audience-resolve-mapper", + "consentRequired": false, + "config": {} + }, + { + "name": "client roles", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-client-role-mapper", + "consentRequired": false, + "config": { + "user.attribute": "foo", + "access.token.claim": "true", + "claim.name": "resource_access.${client_id}.roles", + "jsonType.label": "String", + "multivalued": "true" + } + }, + { + "name": "realm roles", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-realm-role-mapper", + "consentRequired": false, + "config": { + "user.attribute": "foo", + "access.token.claim": "true", + "claim.name": "realm_access.roles", + "jsonType.label": "String", + "multivalued": "true" + } + } + ] + }, + { + "name": "phone", + "description": "OpenID Connect built-in scope: phone", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "display.on.consent.screen": "true", + "consent.screen.text": "${phoneScopeConsentText}" + }, + "protocolMappers": [ + { + "name": "phone number", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "phoneNumber", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "phone_number", + "jsonType.label": "String" + } + }, + { + "name": "phone number verified", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "phoneNumberVerified", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "phone_number_verified", + "jsonType.label": "boolean" + } + } + ] + }, + { + "name": "email", + "description": "OpenID Connect built-in scope: email", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "display.on.consent.screen": "true", + "consent.screen.text": "${emailScopeConsentText}" + }, + "protocolMappers": [ + { + "name": "email", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "email", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "email", + "jsonType.label": "String" + } + }, + { + "name": "email verified", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "emailVerified", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "email_verified", + "jsonType.label": "boolean" + } + } + ] + } + ], + "defaultDefaultClientScopes": [ + "email", + "role_list", + "roles", + "web-origins", + "profile" + ], + "defaultOptionalClientScopes": [ + "address", + "phone", + "microprofile-jwt", + "offline_access" + ], + "browserSecurityHeaders": { + "contentSecurityPolicyReportOnly": "", + "xContentTypeOptions": "nosniff", + "xRobotsTag": "none", + "xFrameOptions": "SAMEORIGIN", + "contentSecurityPolicy": "frame-src 'self'; frame-ancestors 'self'; object-src 'none';", + "xXSSProtection": "1; mode=block", + "strictTransportSecurity": "max-age=31536000; includeSubDomains" + }, + "smtpServer": {}, + "eventsEnabled": false, + "eventsListeners": [ + "jboss-logging" + ], + "enabledEventTypes": [], + "adminEventsEnabled": false, + "adminEventsDetailsEnabled": false, + "identityProviders": [], + "identityProviderMappers": [], + "components": { + "org.keycloak.services.clientregistration.policy.ClientRegistrationPolicy": [ + { + "name": "Trusted Hosts", + "providerId": "trusted-hosts", + "subType": "anonymous", + "subComponents": {}, + "config": { + "host-sending-registration-request-must-match": [ + "true" + ], + "client-uris-must-match": [ + "true" + ] + } + }, + { + "name": "Max Clients Limit", + "providerId": "max-clients", + "subType": "anonymous", + "subComponents": {}, + "config": { + "max-clients": [ + "200" + ] + } + }, + { + "name": "Allowed Client Scopes", + "providerId": "allowed-client-templates", + "subType": "anonymous", + "subComponents": {}, + "config": { + "allow-default-scopes": [ + "true" + ] + } + }, + { + "name": "Allowed Client Scopes", + "providerId": "allowed-client-templates", + "subType": "authenticated", + "subComponents": {}, + "config": { + "allow-default-scopes": [ + "true" + ] + } + }, + { + "name": "Consent Required", + "providerId": "consent-required", + "subType": "anonymous", + "subComponents": {}, + "config": {} + }, + { + "name": "Allowed Protocol Mapper Types", + "providerId": "allowed-protocol-mappers", + "subType": "authenticated", + "subComponents": {}, + "config": { + "allowed-protocol-mapper-types": [ + "oidc-full-name-mapper", + "saml-user-property-mapper", + "oidc-usermodel-attribute-mapper", + "saml-role-list-mapper", + "oidc-usermodel-property-mapper", + "saml-user-attribute-mapper", + "oidc-address-mapper", + "oidc-sha256-pairwise-sub-mapper" + ] + } + }, + { + "name": "Full Scope Disabled", + "providerId": "scope", + "subType": "anonymous", + "subComponents": {}, + "config": {} + }, + { + "name": "Allowed Protocol Mapper Types", + "providerId": "allowed-protocol-mappers", + "subType": "anonymous", + "subComponents": {}, + "config": { + "allowed-protocol-mapper-types": [ + "saml-user-attribute-mapper", + "oidc-full-name-mapper", + "oidc-usermodel-property-mapper", + "saml-user-property-mapper", + "oidc-usermodel-attribute-mapper", + "oidc-address-mapper", + "saml-role-list-mapper", + "oidc-sha256-pairwise-sub-mapper" + ] + } + } + ], + "org.keycloak.userprofile.UserProfileProvider": [ + { + "providerId": "declarative-user-profile", + "subComponents": {}, + "config": {} + } + ], + "org.keycloak.keys.KeyProvider": [ + { + "name": "rsa-enc-generated", + "providerId": "rsa-enc-generated", + "subComponents": {}, + "config": { + "priority": [ + "100" + ], + "algorithm": [ + "RSA-OAEP" + ] + } + }, + { + "name": "aes-generated", + "providerId": "aes-generated", + "subComponents": {}, + "config": { + "priority": [ + "100" + ] + } + }, + { + "name": "hmac-generated", + "providerId": "hmac-generated", + "subComponents": {}, + "config": { + "priority": [ + "100" + ], + "algorithm": [ + "HS256" + ] + } + }, + { + "name": "rsa-generated", + "providerId": "rsa-generated", + "subComponents": {}, + "config": { + "priority": [ + "100" + ] + } + } + ] + }, + "internationalizationEnabled": false, + "supportedLocales": [], + "authenticationFlows": [ + { + "alias": "Account verification options", + "description": "Method with which to verity the existing account", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "idp-email-verification", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticatorFlow": true, + "requirement": "ALTERNATIVE", + "priority": 20, + "flowAlias": "Verify Existing Account by Re-authentication", + "userSetupAllowed": false, + "autheticatorFlow": true + } + ] + }, + { + "alias": "Authentication Options", + "description": "Authentication options.", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "basic-auth", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "basic-auth-otp", + "authenticatorFlow": false, + "requirement": "DISABLED", + "priority": 20, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "auth-spnego", + "authenticatorFlow": false, + "requirement": "DISABLED", + "priority": 30, + "userSetupAllowed": false, + "autheticatorFlow": false + } + ] + }, + { + "alias": "Browser - Conditional OTP", + "description": "Flow to determine if the OTP is required for the authentication", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "conditional-user-configured", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "auth-otp-form", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 20, + "userSetupAllowed": false, + "autheticatorFlow": false + } + ] + }, + { + "alias": "Direct Grant - Conditional OTP", + "description": "Flow to determine if the OTP is required for the authentication", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "conditional-user-configured", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "direct-grant-validate-otp", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 20, + "userSetupAllowed": false, + "autheticatorFlow": false + } + ] + }, + { + "alias": "First broker login - Conditional OTP", + "description": "Flow to determine if the OTP is required for the authentication", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "conditional-user-configured", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "auth-otp-form", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 20, + "userSetupAllowed": false, + "autheticatorFlow": false + } + ] + }, + { + "alias": "Handle Existing Account", + "description": "Handle what to do if there is existing account with same email/username like authenticated identity provider", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "idp-confirm-link", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticatorFlow": true, + "requirement": "REQUIRED", + "priority": 20, + "flowAlias": "Account verification options", + "userSetupAllowed": false, + "autheticatorFlow": true + } + ] + }, + { + "alias": "Reset - Conditional OTP", + "description": "Flow to determine if the OTP should be reset or not. Set to REQUIRED to force.", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "conditional-user-configured", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "reset-otp", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 20, + "userSetupAllowed": false, + "autheticatorFlow": false + } + ] + }, + { + "alias": "User creation or linking", + "description": "Flow for the existing/non-existing user alternatives", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticatorConfig": "create unique user config", + "authenticator": "idp-create-user-if-unique", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticatorFlow": true, + "requirement": "ALTERNATIVE", + "priority": 20, + "flowAlias": "Handle Existing Account", + "userSetupAllowed": false, + "autheticatorFlow": true + } + ] + }, + { + "alias": "Verify Existing Account by Re-authentication", + "description": "Reauthentication of existing account", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "idp-username-password-form", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticatorFlow": true, + "requirement": "CONDITIONAL", + "priority": 20, + "flowAlias": "First broker login - Conditional OTP", + "userSetupAllowed": false, + "autheticatorFlow": true + } + ] + }, + { + "alias": "browser", + "description": "browser based authentication", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "auth-cookie", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "auth-spnego", + "authenticatorFlow": false, + "requirement": "DISABLED", + "priority": 20, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "identity-provider-redirector", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 25, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticatorFlow": true, + "requirement": "ALTERNATIVE", + "priority": 30, + "flowAlias": "forms", + "userSetupAllowed": false, + "autheticatorFlow": true + } + ] + }, + { + "alias": "clients", + "description": "Base authentication for clients", + "providerId": "client-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "client-secret", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "client-jwt", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 20, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "client-secret-jwt", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 30, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "client-x509", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 40, + "userSetupAllowed": false, + "autheticatorFlow": false + } + ] + }, + { + "alias": "direct grant", + "description": "OpenID Connect Resource Owner Grant", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "direct-grant-validate-username", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "direct-grant-validate-password", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 20, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticatorFlow": true, + "requirement": "CONDITIONAL", + "priority": 30, + "flowAlias": "Direct Grant - Conditional OTP", + "userSetupAllowed": false, + "autheticatorFlow": true + } + ] + }, + { + "alias": "docker auth", + "description": "Used by Docker clients to authenticate against the IDP", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "docker-http-basic-authenticator", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + } + ] + }, + { + "alias": "first broker login", + "description": "Actions taken after first broker login with identity provider account, which is not yet linked to any Keycloak account", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticatorConfig": "review profile config", + "authenticator": "idp-review-profile", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticatorFlow": true, + "requirement": "REQUIRED", + "priority": 20, + "flowAlias": "User creation or linking", + "userSetupAllowed": false, + "autheticatorFlow": true + } + ] + }, + { + "alias": "forms", + "description": "Username, password, otp and other auth forms.", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "auth-username-password-form", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticatorFlow": true, + "requirement": "CONDITIONAL", + "priority": 20, + "flowAlias": "Browser - Conditional OTP", + "userSetupAllowed": false, + "autheticatorFlow": true + } + ] + }, + { + "alias": "http challenge", + "description": "An authentication flow based on challenge-response HTTP Authentication Schemes", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "no-cookie-redirect", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticatorFlow": true, + "requirement": "REQUIRED", + "priority": 20, + "flowAlias": "Authentication Options", + "userSetupAllowed": false, + "autheticatorFlow": true + } + ] + }, + { + "alias": "registration", + "description": "registration flow", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "registration-page-form", + "authenticatorFlow": true, + "requirement": "REQUIRED", + "priority": 10, + "flowAlias": "registration form", + "userSetupAllowed": false, + "autheticatorFlow": true + } + ] + }, + { + "alias": "registration form", + "description": "registration form", + "providerId": "form-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "registration-user-creation", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 20, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "registration-profile-action", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 40, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "registration-password-action", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 50, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "registration-recaptcha-action", + "authenticatorFlow": false, + "requirement": "DISABLED", + "priority": 60, + "userSetupAllowed": false, + "autheticatorFlow": false + } + ] + }, + { + "alias": "reset credentials", + "description": "Reset credentials for a user if they forgot their password or something", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "reset-credentials-choose-user", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "reset-credential-email", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 20, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "reset-password", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 30, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticatorFlow": true, + "requirement": "CONDITIONAL", + "priority": 40, + "flowAlias": "Reset - Conditional OTP", + "userSetupAllowed": false, + "autheticatorFlow": true + } + ] + }, + { + "alias": "saml ecp", + "description": "SAML ECP Profile Authentication Flow", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "http-basic-authenticator", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + } + ] + } + ], + "authenticatorConfig": [ + { + "alias": "create unique user config", + "config": { + "require.password.update.after.registration": "false" + } + }, + { + "alias": "review profile config", + "config": { + "update.profile.on.first.login": "missing" + } + } + ], + "requiredActions": [ + { + "alias": "CONFIGURE_TOTP", + "name": "Configure OTP", + "providerId": "CONFIGURE_TOTP", + "enabled": true, + "defaultAction": false, + "priority": 10, + "config": {} + }, + { + "alias": "terms_and_conditions", + "name": "Terms and Conditions", + "providerId": "terms_and_conditions", + "enabled": false, + "defaultAction": false, + "priority": 20, + "config": {} + }, + { + "alias": "UPDATE_PASSWORD", + "name": "Update Password", + "providerId": "UPDATE_PASSWORD", + "enabled": true, + "defaultAction": false, + "priority": 30, + "config": {} + }, + { + "alias": "UPDATE_PROFILE", + "name": "Update Profile", + "providerId": "UPDATE_PROFILE", + "enabled": true, + "defaultAction": false, + "priority": 40, + "config": {} + }, + { + "alias": "VERIFY_EMAIL", + "name": "Verify Email", + "providerId": "VERIFY_EMAIL", + "enabled": true, + "defaultAction": false, + "priority": 50, + "config": {} + }, + { + "alias": "delete_account", + "name": "Delete Account", + "providerId": "delete_account", + "enabled": false, + "defaultAction": false, + "priority": 60, + "config": {} + }, + { + "alias": "update_user_locale", + "name": "Update User Locale", + "providerId": "update_user_locale", + "enabled": true, + "defaultAction": false, + "priority": 1000, + "config": {} + } + ], + "browserFlow": "browser", + "registrationFlow": "registration", + "directGrantFlow": "direct grant", + "resetCredentialsFlow": "reset credentials", + "clientAuthenticationFlow": "clients", + "dockerAuthenticationFlow": "docker auth", + "attributes": { + "cibaBackchannelTokenDeliveryMode": "poll", + "cibaExpiresIn": "120", + "cibaAuthRequestedUserHint": "login_hint", + "oauth2DeviceCodeLifespan": "600", + "clientOfflineSessionMaxLifespan": "0", + "oauth2DevicePollingInterval": "5", + "clientSessionIdleTimeout": "0", + "parRequestUriLifespan": "60", + "clientSessionMaxLifespan": "0", + "frontendUrl": "https://solaceoauth:10443/auth/", + "clientOfflineSessionIdleTimeout": "0", + "cibaInterval": "5" + }, + "keycloakVersion": "16.1.1", + "userManagedAccessAllowed": false, + "clientProfiles": { + "profiles": [] + }, + "clientPolicies": { + "policies": [] + }, + "users": [ + { + "username": "admin", + "enabled": true, + "credentials": [ + { + "type": "mysecret!", + "value": "mysecret!" + } + ], + "clientRoles": { + "realm-management": [ + "realm-admin" + ], + "account": [ + "manage-account" + ] + } + } + ] +} \ No newline at end of file diff --git a/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/resources/oauth2/oauth/nginx.conf b/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/resources/oauth2/oauth/nginx.conf new file mode 100644 index 00000000..a3ba5e27 --- /dev/null +++ b/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/resources/oauth2/oauth/nginx.conf @@ -0,0 +1,39 @@ +events { +} +http { + server { + listen 1080; + + server_name _; + return 301 https://$host:10443$request_uri; # redirect http requests to https + } + server { + + include /etc/nginx/mime.types; + + listen 10443 ssl; + + ssl_ciphers ALL:@SECLEVEL=0; #required for self signed certs and/or for certs signed with weak ciphers + ssl_protocols TLSv1.2 TLSv1.3; + ssl_certificate /etc/sslcerts/keycloak.pem; + ssl_certificate_key /etc/sslcerts/keycloak.key; + + add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; + add_header Content-Security-Policy "default-src 'self'; frame-ancestors 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src http://example.com;"; + #add_header X-Content-Type-Options nosniff; # cannot apply now because of open keycloak issue https://issues.redhat.com/browse/KEYCLOAK-17076 + add_header X-XSS-Protection: "1; mode=block"; + + proxy_set_header X-Forwarded-For $proxy_protocol_addr; # To forward the original client's IP address + proxy_set_header X-Forwarded-Proto $scheme; # to forward the original protocol (HTTP or HTTPS) + proxy_set_header Host $host:$server_port; # to forward the original host requested by the client + + location / { + root /data/www; + try_files $uri $uri/ /index.html; #to support in app routing in SPA + } + + location /auth { + proxy_pass http://keycloak:8080; #redirect urls starting with /auth to keycloak + } + } +} \ No newline at end of file diff --git a/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/resources/oauth2/oauth/www/index.html b/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/resources/oauth2/oauth/www/index.html new file mode 100644 index 00000000..e6047aa1 --- /dev/null +++ b/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/resources/oauth2/oauth/www/index.html @@ -0,0 +1,6 @@ + + + Hello World!! + + + \ No newline at end of file diff --git a/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/resources/oauth2/solace.env b/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/resources/oauth2/solace.env new file mode 100644 index 00000000..04a0dfee --- /dev/null +++ b/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/resources/oauth2/solace.env @@ -0,0 +1,4 @@ +username_admin_globalaccesslevel=admin +username_admin_password=admin +system_scaling_maxconnectioncount=1000 +webmanager_redirecthttp_enable=false diff --git a/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/resources/oauth2/solace_tls.env b/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/resources/oauth2/solace_tls.env new file mode 100644 index 00000000..cc90d8a8 --- /dev/null +++ b/solace-spring-cloud-stream-binder/solace-spring-cloud-stream-binder/src/test/resources/oauth2/solace_tls.env @@ -0,0 +1,5 @@ +username_admin_globalaccesslevel=admin +username_admin_password=admin +system_scaling_maxconnectioncount=1000 +tls_servercertificate_filepath=/run/secrets/server.pem +webmanager_redirecthttp_enable=false