From 39febc560b09ae0c46a4e5e52d8a6ad27dbdeab4 Mon Sep 17 00:00:00 2001 From: mayur-solace <81177023+mayur-solace@users.noreply.github.com> Date: Thu, 18 Jul 2024 11:26:11 -0400 Subject: [PATCH] Add OAuth2 support for authentication with Solace PubSub+ Broker. (#133) * Add OAuth2 support for authentication with Solace PubSub+ Broker * Disable overwrite settings in setup-java (#40) * Updated dev docs. * Fix action permissions (#41) * fix manual test support install version * Remove unwanted test dependency as requested in https://github.com/SolaceProducts/solace-spring-boot/issues/101 Use slf4j instead of apache logging --------- Co-authored-by: Jeffrey D <11084623+Nephery@users.noreply.github.com> --- .github/workflows/build-test.yml | 27 +- README.md | 1 + maven/settings.xml | 9 - pom.xml | 21 +- .../pom.xml | 55 +- .../SolaceJavaAutoConfiguration.java | 15 +- .../autoconfigure/SolaceJavaProperties.java | 17 + .../SolaceOAuthClientConfiguration.java | 70 + ...efaultSolaceOAuth2SessionEventHandler.java | 73 + ...faultSolaceSessionOAuth2TokenProvider.java | 76 + .../SolaceOAuth2SessionEventHandler.java | 17 + .../SolaceSessionOAuth2TokenProvider.java | 16 + .../jcsmp/SpringJCSMPFactory.java | 171 +- .../it/util/semp/SempClientException.java | 135 + .../config/BrokerConfiguratorBuilder.java | 869 +++++++ .../semp/monitor/BrokerMonitorBuilder.java | 374 +++ .../SolaceJavaAutoConfigurationTest.java | 17 +- ...okerTestContainerWithTlsAndOAuthSetup.java | 117 + ...eeTierBrokerTestContainerWithTlsSetup.java | 99 + .../MessagingWithClientCertAuthIT.java | 109 + .../springBootTests/MessagingWithOAuthIT.java | 287 +++ .../springBootTests/SampleApp.java | 14 + ...ltSolaceOAuth2SessionEventHandlerTest.java | 67 + .../application-clientCertAuthIT.yml | 21 + .../test/resources/application-oauthIT.yml | 27 + .../src/test/resources/certs/README.txt | 96 + .../test/resources/certs/broker/solbroker.crt | 28 + .../test/resources/certs/broker/solbroker.csr | 20 + .../test/resources/certs/broker/solbroker.key | 27 + .../test/resources/certs/broker/solbroker.pem | 55 + .../certs/client/client-keystore.jks | Bin 0 -> 2441 bytes .../certs/client/client-truststore.p12 | Bin 0 -> 1814 bytes .../test/resources/certs/client/client.crt | 25 + .../test/resources/certs/client/client.csr | 17 + .../test/resources/certs/client/client.key | 27 + .../test/resources/certs/client/client.p12 | Bin 0 -> 2754 bytes .../test/resources/certs/client/client.pem | 52 + .../resources/certs/keycloak/keycloak.crt | 30 + .../resources/certs/keycloak/keycloak.csr | 21 + .../resources/certs/keycloak/keycloak.key | 27 + .../resources/certs/keycloak/keycloak.pem | 57 + .../test/resources/certs/keycloak_san.conf | 30 + .../test/resources/certs/rootCA/rootCA.crt | 32 + .../test/resources/certs/rootCA/rootCA.der | Bin 0 -> 1435 bytes .../test/resources/certs/rootCA/rootCA.key | 51 + .../test/resources/certs/rootCA/rootCA.pem | 83 + .../test/resources/certs/rootCA/rootCA.srl | 1 + .../test/resources/certs/solbroker_san.conf | 26 + ...oker-with-tls-and-oauth-docker-compose.yml | 52 + ...ee-tier-broker-with-tls-docker-compose.yml | 26 + .../src/test/resources/logback-test.xml | 15 + .../src/test/resources/oauth/README | 60 + ...uth-resource-server-role-realm-export.json | 2276 +++++++++++++++++ .../src/test/resources/oauth/nginx.conf | 39 + .../src/test/resources/oauth/www/index.html | 6 + .../src/test/resources/solace.env | 4 + .../src/test/resources/solace_tls.env | 5 + .../pom.xml | 10 +- .../SolaceJmsAutoConfigurationTest.java | 16 +- solace-spring-boot-bom/README.md | 7 +- solace-spring-boot-bom/pom.xml | 4 +- solace-spring-boot-parent/pom.xml | 61 +- solace-spring-boot-samples/pom.xml | 2 +- .../solace-java-oauth2-sample-app/README.md | 39 + .../solace-java-oauth2-sample-app/pom.xml | 105 + .../src/main/java/demo/DemoApplication.java | 137 + .../main/java/demo/DemoMessageConsumer.java | 54 + .../java/demo/DemoPublishEventHandler.java | 37 + .../src/main/resources/application.yml | 32 + .../solace-java-sample-app/pom.xml | 20 +- .../solace-jms-sample-app-jndi/pom.xml | 20 +- .../solace-jms-sample-app/pom.xml | 20 +- .../solace-java-spring-boot-starter/README.md | 102 +- .../solace-java-spring-boot-starter/pom.xml | 4 +- .../solace-jms-spring-boot-starter/README.md | 4 +- .../solace-jms-spring-boot-starter/pom.xml | 4 +- .../solace-spring-boot-starter/pom.xml | 4 +- 77 files changed, 6373 insertions(+), 201 deletions(-) create mode 100644 solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/main/java/com/solace/spring/boot/autoconfigure/SolaceOAuthClientConfiguration.java create mode 100644 solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/main/java/com/solacesystems/jcsmp/DefaultSolaceOAuth2SessionEventHandler.java create mode 100644 solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/main/java/com/solacesystems/jcsmp/DefaultSolaceSessionOAuth2TokenProvider.java create mode 100644 solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/main/java/com/solacesystems/jcsmp/SolaceOAuth2SessionEventHandler.java create mode 100644 solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/main/java/com/solacesystems/jcsmp/SolaceSessionOAuth2TokenProvider.java create mode 100644 solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/java/com/solace/it/util/semp/SempClientException.java create mode 100644 solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/java/com/solace/it/util/semp/config/BrokerConfiguratorBuilder.java create mode 100644 solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/java/com/solace/it/util/semp/monitor/BrokerMonitorBuilder.java create mode 100644 solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/java/com/solace/spring/boot/autoconfigure/springBootTests/MessagingServiceFreeTierBrokerTestContainerWithTlsAndOAuthSetup.java create mode 100644 solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/java/com/solace/spring/boot/autoconfigure/springBootTests/MessagingServiceFreeTierBrokerTestContainerWithTlsSetup.java create mode 100644 solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/java/com/solace/spring/boot/autoconfigure/springBootTests/MessagingWithClientCertAuthIT.java create mode 100644 solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/java/com/solace/spring/boot/autoconfigure/springBootTests/MessagingWithOAuthIT.java create mode 100644 solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/java/com/solace/spring/boot/autoconfigure/springBootTests/SampleApp.java create mode 100644 solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/java/com/solacesystems/jcsmp/DefaultSolaceOAuth2SessionEventHandlerTest.java create mode 100644 solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/application-clientCertAuthIT.yml create mode 100644 solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/application-oauthIT.yml create mode 100644 solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/certs/README.txt create mode 100644 solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/certs/broker/solbroker.crt create mode 100644 solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/certs/broker/solbroker.csr create mode 100644 solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/certs/broker/solbroker.key create mode 100644 solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/certs/broker/solbroker.pem create mode 100644 solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/certs/client/client-keystore.jks create mode 100644 solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/certs/client/client-truststore.p12 create mode 100644 solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/certs/client/client.crt create mode 100644 solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/certs/client/client.csr create mode 100644 solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/certs/client/client.key create mode 100644 solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/certs/client/client.p12 create mode 100644 solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/certs/client/client.pem create mode 100644 solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/certs/keycloak/keycloak.crt create mode 100644 solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/certs/keycloak/keycloak.csr create mode 100644 solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/certs/keycloak/keycloak.key create mode 100644 solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/certs/keycloak/keycloak.pem create mode 100644 solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/certs/keycloak_san.conf create mode 100644 solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/certs/rootCA/rootCA.crt create mode 100644 solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/certs/rootCA/rootCA.der create mode 100644 solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/certs/rootCA/rootCA.key create mode 100644 solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/certs/rootCA/rootCA.pem create mode 100644 solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/certs/rootCA/rootCA.srl create mode 100644 solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/certs/solbroker_san.conf create mode 100644 solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/free-tier-broker-with-tls-and-oauth-docker-compose.yml create mode 100644 solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/free-tier-broker-with-tls-docker-compose.yml create mode 100644 solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/logback-test.xml create mode 100644 solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/oauth/README create mode 100644 solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/oauth/keycloak/solace-oauth-resource-server-role-realm-export.json create mode 100644 solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/oauth/nginx.conf create mode 100644 solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/oauth/www/index.html create mode 100644 solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/solace.env create mode 100644 solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/solace_tls.env create mode 100644 solace-spring-boot-samples/solace-java-oauth2-sample-app/README.md create mode 100644 solace-spring-boot-samples/solace-java-oauth2-sample-app/pom.xml create mode 100644 solace-spring-boot-samples/solace-java-oauth2-sample-app/src/main/java/demo/DemoApplication.java create mode 100644 solace-spring-boot-samples/solace-java-oauth2-sample-app/src/main/java/demo/DemoMessageConsumer.java create mode 100644 solace-spring-boot-samples/solace-java-oauth2-sample-app/src/main/java/demo/DemoPublishEventHandler.java create mode 100644 solace-spring-boot-samples/solace-java-oauth2-sample-app/src/main/resources/application.yml diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index 9397fa8..87164ec 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -5,10 +5,23 @@ name: build on: pull_request: push: + workflow_dispatch: jobs: - build: + dupe_check: + name: Check for Duplicate Workflow Run + runs-on: ubuntu-latest + outputs: + should_skip: ${{ steps.skip_check.outputs.should_skip }} + steps: + - id: skip_check + uses: fkirc/skip-duplicate-actions@v5.3.1 + with: + concurrent_skipping: same_content + do_not_skip: '["pull_request", "workflow_dispatch", "schedule"]' + build: + if: needs.dupe_check.outputs.should_skip != 'true' runs-on: ubuntu-latest steps: @@ -18,7 +31,19 @@ jobs: with: distribution: 'zulu' java-version: '17' + overwrite-settings: false cache: 'maven' + - name: Manually Install Test Support If Necessary + if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name + run: | + sudo apt-get update -qq + sudo apt-get install -y libxml2-utils + version="$(xmllint --xpath '/*[local-name()="project"]/*[local-name()="properties"]/*[local-name()="solace.integration.test.support.version"]/text()' pom.xml)" + echo "Detected test support version: ${version}" + + git clone --depth 1 --branch "${version}" https://github.com/SolaceDev/solace-integration-test-support.git + cd "${GITHUB_WORKSPACE}/solace-integration-test-support" + mvn install -Dchangelist= -DskipTests - name: Build and run Tests run: mvn clean verify --settings "${GITHUB_WORKSPACE}/maven/settings.xml" env: diff --git a/README.md b/README.md index 1f915c3..483328c 100644 --- a/README.md +++ b/README.md @@ -71,6 +71,7 @@ solace-spring-boot-build (root) --> solace-java-sample-app --> solace-jms-sample-app --> solace-jms-sample-app-jndi + --> solace-java-oauth2-sample-app Where: <-- indicates the parent of the project diff --git a/maven/settings.xml b/maven/settings.xml index 90751f6..95753e3 100644 --- a/maven/settings.xml +++ b/maven/settings.xml @@ -29,14 +29,5 @@ ${env.GITHUB_ACTOR} ${env.GITHUB_TOKEN} - - ossrh - ${env.MAVEN_OSSRH_USER} - ${env.MAVEN_OSSRH_PASS} - - - gpg.passphrase - ${env.GPG_PASSPHRASE} - diff --git a/pom.xml b/pom.xml index fedf821..bf31f4e 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.solace.spring.boot solace-spring-boot-build - 2.0.1-SNAPSHOT + 2.1.0-SNAPSHOT pom Solace Spring Boot Build @@ -15,12 +15,14 @@ SolaceProducts - 3.0.6 + 3.3.1 - 5.0.1-SNAPSHOT + 5.1.0-SNAPSHOT - 5.0.1-SNAPSHOT - 2.0.1-SNAPSHOT + 5.1.0-SNAPSHOT + 2.1.0-SNAPSHOT + 1.19.8 + 1.1.2 @@ -54,6 +56,7 @@ solace-spring-boot-samples/solace-java-sample-app solace-spring-boot-samples/solace-jms-sample-app solace-spring-boot-samples/solace-jms-sample-app-jndi + solace-spring-boot-samples/solace-java-oauth2-sample-app solace-spring-boot-starters/solace-java-spring-boot-starter solace-spring-boot-starters/solace-jms-spring-boot-starter solace-spring-boot-starters/solace-spring-boot-starter @@ -85,6 +88,14 @@ solace-java-spring-boot-starter ${solace.spring.boot.java-starter.version} + + + com.solace.test.integration + solace-integration-test-support-bom + ${solace.integration.test.support.version} + pom + import + diff --git a/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/pom.xml b/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/pom.xml index 5665d26..4fa5c47 100644 --- a/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/pom.xml +++ b/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/pom.xml @@ -5,12 +5,12 @@ com.solace.spring.boot solace-spring-boot-parent - 2.0.1-SNAPSHOT + 2.1.0-SNAPSHOT ../../solace-spring-boot-parent/pom.xml solace-java-spring-boot-autoconfigure - 5.0.1-SNAPSHOT + 5.1.0-SNAPSHOT jar Solace Spring Boot Autoconfiguration - Java @@ -34,16 +34,61 @@ ${solace.jcsmp.version} + + org.springframework.boot + spring-boot-starter-oauth2-client + true + + + org.springframework.boot spring-boot-starter-test test - com.github.stefanbirkner - system-rules - 1.19.0 + org.springframework.boot + spring-boot-starter-web + test + + + org.junit-pioneer + junit-pioneer + 1.9.1 + test + + + org.testcontainers + testcontainers + ${testcontainers.version} + test + + + org.testcontainers + junit-jupiter + ${testcontainers.version} + test + + + com.solace.test.integration + pubsubplus-junit-jupiter + ${solace.integration.test.support.version} + test + + + org.testcontainers + testcontainers + ${testcontainers.version} test + + + + + org.apache.maven.plugins + maven-failsafe-plugin + + + diff --git a/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/main/java/com/solace/spring/boot/autoconfigure/SolaceJavaAutoConfiguration.java b/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/main/java/com/solace/spring/boot/autoconfigure/SolaceJavaAutoConfiguration.java index d56e188..f29399c 100644 --- a/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/main/java/com/solace/spring/boot/autoconfigure/SolaceJavaAutoConfiguration.java +++ b/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/main/java/com/solace/spring/boot/autoconfigure/SolaceJavaAutoConfiguration.java @@ -20,6 +20,7 @@ import com.solacesystems.jcsmp.JCSMPChannelProperties; import com.solacesystems.jcsmp.JCSMPProperties; +import com.solacesystems.jcsmp.SolaceSessionOAuth2TokenProvider; import com.solacesystems.jcsmp.SpringJCSMPFactory; import java.util.Map; import java.util.Map.Entry; @@ -33,12 +34,15 @@ import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import org.springframework.lang.Nullable; @Configuration @AutoConfigureBefore(JmsAutoConfiguration.class) @ConditionalOnClass({JCSMPProperties.class}) @ConditionalOnMissingBean(SpringJCSMPFactory.class) @EnableConfigurationProperties(SolaceJavaProperties.class) +@Import(SolaceOAuthClientConfiguration.class) public class SolaceJavaAutoConfiguration { private SolaceJavaProperties properties; @@ -54,8 +58,9 @@ public SolaceJavaAutoConfiguration(SolaceJavaProperties properties) { * @return {@link SpringJCSMPFactory} based on {@link JCSMPProperties} bean. */ @Bean - public SpringJCSMPFactory getSpringJCSMPFactory(JCSMPProperties jcsmpProperties) { - return new SpringJCSMPFactory(jcsmpProperties); + public SpringJCSMPFactory getSpringJCSMPFactory(JCSMPProperties jcsmpProperties, + @Nullable SolaceSessionOAuth2TokenProvider solaceSessionOAuth2TokenProvider) { + return new SpringJCSMPFactory(jcsmpProperties, solaceSessionOAuth2TokenProvider); } /** @@ -87,6 +92,12 @@ public JCSMPProperties getJCSMPProperties() { cp.setReconnectRetries(properties.getReconnectRetries()); cp.setConnectRetriesPerHost(properties.getConnectRetriesPerHost()); cp.setReconnectRetryWaitInMillis(properties.getReconnectRetryWaitInMillis()); + + if (properties.getOauth2ClientRegistrationId() != null) { + jcsmpProps.setProperty(SolaceJavaProperties.SPRING_OAUTH2_CLIENT_REGISTRATION_ID, + properties.getOauth2ClientRegistrationId()); + } + return jcsmpProps; } diff --git a/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/main/java/com/solace/spring/boot/autoconfigure/SolaceJavaProperties.java b/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/main/java/com/solace/spring/boot/autoconfigure/SolaceJavaProperties.java index 2a380fc..b617401 100644 --- a/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/main/java/com/solace/spring/boot/autoconfigure/SolaceJavaProperties.java +++ b/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/main/java/com/solace/spring/boot/autoconfigure/SolaceJavaProperties.java @@ -27,6 +27,8 @@ @ConfigurationProperties("solace.java") public class SolaceJavaProperties { + public static final String SPRING_OAUTH2_CLIENT_REGISTRATION_ID = "SPRING_OAUTH2_CLIENT_REGISTRATION_ID"; + /** * Solace Message Router Host address. Port is optional and intelligently defaulted by the Solace Java API. */ @@ -99,7 +101,22 @@ public class SolaceJavaProperties { */ private final Map apiProperties = new ConcurrentHashMap<>(); + /** + * The Spring Security OAuth2 Client Registration Id + * spring.security.oauth2.client.registration.<registration-id> to use for OAuth2 + * token + * retrieval. This field is required when the Solace session is configured to use OAuth2 via + * solace.java.apiProperties.authentication_scheme=AUTHENTICATION_SCHEME_OAUTH2 + */ + private String oauth2ClientRegistrationId; + + public String getOauth2ClientRegistrationId() { + return oauth2ClientRegistrationId; + } + public void setOauth2ClientRegistrationId(String oauth2ClientRegistrationId) { + this.oauth2ClientRegistrationId = oauth2ClientRegistrationId; + } public String getHost() { return host; diff --git a/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/main/java/com/solace/spring/boot/autoconfigure/SolaceOAuthClientConfiguration.java b/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/main/java/com/solace/spring/boot/autoconfigure/SolaceOAuthClientConfiguration.java new file mode 100644 index 0000000..a96a62f --- /dev/null +++ b/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/main/java/com/solace/spring/boot/autoconfigure/SolaceOAuthClientConfiguration.java @@ -0,0 +1,70 @@ +package com.solace.spring.boot.autoconfigure; + +import com.solacesystems.jcsmp.DefaultSolaceSessionOAuth2TokenProvider; +import com.solacesystems.jcsmp.JCSMPProperties; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.autoconfigure.security.oauth2.client.servlet.OAuth2ClientAutoConfiguration; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import org.springframework.security.oauth2.client.AuthorizedClientServiceOAuth2AuthorizedClientManager; +import org.springframework.security.oauth2.client.OAuth2AuthorizedClientProvider; +import org.springframework.security.oauth2.client.OAuth2AuthorizedClientProviderBuilder; +import org.springframework.security.oauth2.client.OAuth2AuthorizedClientService; +import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository; + +/** + * Configuration class for Solace OAuth client. This configuration is only active when the + * 'solace.java.apiProperties.AUTHENTICATION_SCHEME' property is set to + * 'AUTHENTICATION_SCHEME_OAUTH2'. + */ +@Configuration +@ConditionalOnProperty(prefix = "solace.java.apiProperties", name = "AUTHENTICATION_SCHEME", + havingValue = "AUTHENTICATION_SCHEME_OAUTH2") +@Import(OAuth2ClientAutoConfiguration.class) +public class SolaceOAuthClientConfiguration { + + /** + * Creates and configures an OAuth2AuthorizedClientManager for Solace session. This manager is + * configured with OAuth2AuthorizedClientProvider for client credentials and refresh token. + * + * @param clientRegistrationRepository Repository of client registrations. + * @param oAuth2AuthorizedClientService Service for authorized OAuth2 clients. + * @return Configured OAuth2AuthorizedClientManager. + */ + @Bean + public AuthorizedClientServiceOAuth2AuthorizedClientManager solaceOAuthAuthorizedClientServiceAndManager( + ClientRegistrationRepository clientRegistrationRepository, + OAuth2AuthorizedClientService oAuth2AuthorizedClientService) { + final OAuth2AuthorizedClientProvider clientCredentialsAuthorizedClientProvider = + OAuth2AuthorizedClientProviderBuilder.builder() + .authorizationCode() + .clientCredentials() + .refreshToken() + .build(); + + final AuthorizedClientServiceOAuth2AuthorizedClientManager authorizedClientManager = + new AuthorizedClientServiceOAuth2AuthorizedClientManager( + clientRegistrationRepository, oAuth2AuthorizedClientService); + authorizedClientManager.setAuthorizedClientProvider(clientCredentialsAuthorizedClientProvider); + + return authorizedClientManager; + } + + /** + * Creates a SolaceSessionOAuth2TokenProvider for providing OAuth2 access tokens for Solace + * sessions. + * + * @param jcsmpProperties The JCSMP properties. + * @param solaceOAuthAuthorizedClientServiceAndManager The OAuth2AuthorizedClientManager for + * Solace session. + * @return Configured SolaceSessionOAuth2TokenProvider. + */ + @Bean + public DefaultSolaceSessionOAuth2TokenProvider solaceSessionOAuth2TokenProvider( + JCSMPProperties jcsmpProperties, + AuthorizedClientServiceOAuth2AuthorizedClientManager solaceOAuthAuthorizedClientServiceAndManager) { + return new DefaultSolaceSessionOAuth2TokenProvider(jcsmpProperties, + solaceOAuthAuthorizedClientServiceAndManager); + } +} \ No newline at end of file diff --git a/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/main/java/com/solacesystems/jcsmp/DefaultSolaceOAuth2SessionEventHandler.java b/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/main/java/com/solacesystems/jcsmp/DefaultSolaceOAuth2SessionEventHandler.java new file mode 100644 index 0000000..40ba2e8 --- /dev/null +++ b/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/main/java/com/solacesystems/jcsmp/DefaultSolaceOAuth2SessionEventHandler.java @@ -0,0 +1,73 @@ +package com.solacesystems.jcsmp; + +import static com.solacesystems.jcsmp.JCSMPProperties.AUTHENTICATION_SCHEME; +import static com.solacesystems.jcsmp.JCSMPProperties.AUTHENTICATION_SCHEME_OAUTH2; +import static com.solacesystems.jcsmp.SessionEvent.RECONNECTING; +import java.util.Objects; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + + +/** + * Default implementation of SolaceOAuth2SessionEventHandler. This class handles the OAuth2 token + * refresh logic when the session is reconnecting. + */ +public class DefaultSolaceOAuth2SessionEventHandler implements SolaceOAuth2SessionEventHandler { + + private static final Logger logger = LoggerFactory.getLogger(DefaultSolaceOAuth2SessionEventHandler.class); + + protected final SolaceSessionOAuth2TokenProvider solaceSessionOAuth2TokenProvider; + protected final JCSMPProperties jcsmpProperties; + protected JCSMPSession jcsmpSession; + + /** + * Constructs a new DefaultSolaceOAuth2SessionEventHandler with the provided JCSMP properties and + * OAuth2 token provider. + * + * @param jcsmpProperties The JCSMP properties. + * @param solaceSessionOAuth2TokenProvider The OAuth2 token provider. + */ + public DefaultSolaceOAuth2SessionEventHandler(JCSMPProperties jcsmpProperties, + SolaceSessionOAuth2TokenProvider solaceSessionOAuth2TokenProvider) { + this.jcsmpProperties = jcsmpProperties; + this.solaceSessionOAuth2TokenProvider = solaceSessionOAuth2TokenProvider; + + Objects.requireNonNull(jcsmpProperties); + if (isAuthSchemeOAuth2()) { + Objects.requireNonNull(solaceSessionOAuth2TokenProvider); + } + } + + @Override + public void handleEvent(SessionEventArgs sessionEventArgs) { + final SessionEvent event = sessionEventArgs.getEvent(); + if (event == RECONNECTING && isAuthSchemeOAuth2()) { + refreshOAuth2AccessToken(); + } + } + + protected boolean isAuthSchemeOAuth2() { + return AUTHENTICATION_SCHEME_OAUTH2.equalsIgnoreCase( + jcsmpProperties.getStringProperty(AUTHENTICATION_SCHEME)); + } + + private void refreshOAuth2AccessToken() { + try { + if (logger.isDebugEnabled()) { + logger.debug("Refreshing OAuth2 access token"); + } + + final String newAccessToken = solaceSessionOAuth2TokenProvider.getAccessToken(); + this.jcsmpSession.setProperty(JCSMPProperties.OAUTH2_ACCESS_TOKEN, newAccessToken); + } catch (JCSMPException e) { + if (logger.isDebugEnabled()) { + logger.debug("Exception while fetching/providing refreshed access token: ", e); + } + } + } + + @Override + public void setJcsmpSession(JCSMPSession jcsmpSession) { + this.jcsmpSession = jcsmpSession; + } +} \ No newline at end of file diff --git a/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/main/java/com/solacesystems/jcsmp/DefaultSolaceSessionOAuth2TokenProvider.java b/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/main/java/com/solacesystems/jcsmp/DefaultSolaceSessionOAuth2TokenProvider.java new file mode 100644 index 0000000..5ee5809 --- /dev/null +++ b/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/main/java/com/solacesystems/jcsmp/DefaultSolaceSessionOAuth2TokenProvider.java @@ -0,0 +1,76 @@ +package com.solacesystems.jcsmp; + +import static com.solacesystems.jcsmp.JCSMPProperties.USERNAME; +import com.solace.spring.boot.autoconfigure.SolaceJavaProperties; +import java.util.Objects; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.security.oauth2.client.AuthorizedClientServiceOAuth2AuthorizedClientManager; +import org.springframework.security.oauth2.client.OAuth2AuthorizeRequest; +import org.springframework.security.oauth2.client.OAuth2AuthorizedClient; +import org.springframework.security.oauth2.core.OAuth2AccessToken; + +/** + * Default implementation of SolaceSessionOAuth2TokenProvider. This class fetches and returns the + * current OAuth2 access token using the provided JCSMP properties and OAuth2 authorized client + * manager. + */ +public class DefaultSolaceSessionOAuth2TokenProvider implements SolaceSessionOAuth2TokenProvider { + + private static final Logger logger = LoggerFactory.getLogger( + DefaultSolaceSessionOAuth2TokenProvider.class); + + private final JCSMPProperties jcsmpProperties; + private final AuthorizedClientServiceOAuth2AuthorizedClientManager solaceOAuthAuthorizedClientServiceAndManager; + + /** + * Constructs a new DefaultSolaceSessionOAuth2TokenProvider with the provided JCSMP properties and + * OAuth2 authorized client manager. + * + * @param jcsmpProperties The JCSMP properties. + * @param solaceOAuthAuthorizedClientServiceAndManager The OAuth2 authorized client manager. + */ + public DefaultSolaceSessionOAuth2TokenProvider(JCSMPProperties jcsmpProperties, + AuthorizedClientServiceOAuth2AuthorizedClientManager solaceOAuthAuthorizedClientServiceAndManager) { + Objects.requireNonNull(jcsmpProperties); + Objects.requireNonNull(solaceOAuthAuthorizedClientServiceAndManager); + this.jcsmpProperties = jcsmpProperties; + this.solaceOAuthAuthorizedClientServiceAndManager = solaceOAuthAuthorizedClientServiceAndManager; + } + + @Override + public String getAccessToken() { + try { + final String clientUserName = Objects.toString( + jcsmpProperties.getStringProperty(USERNAME), "spring-default-client-username"); + final String oauth2ClientRegistrationId = jcsmpProperties + .getStringProperty(SolaceJavaProperties.SPRING_OAUTH2_CLIENT_REGISTRATION_ID); + + if (logger.isInfoEnabled()) { + logger.info(String.format("Fetching OAuth2 access token using client registration ID: %s", + oauth2ClientRegistrationId)); + } + + final OAuth2AuthorizeRequest authorizeRequest = + OAuth2AuthorizeRequest.withClientRegistrationId(oauth2ClientRegistrationId) + .principal(clientUserName) + .build(); + + //Perform the actual authorization request using the authorized client service and authorized + //client manager. This is where the JWT is retrieved from the OAuth/OIDC servers. + final OAuth2AuthorizedClient oAuth2AuthorizedClient = + solaceOAuthAuthorizedClientServiceAndManager.authorize(authorizeRequest); + + //Get the token from the authorized client object + final OAuth2AccessToken accessToken = Objects.requireNonNull(oAuth2AuthorizedClient) + .getAccessToken(); + + return accessToken.getTokenValue(); + } catch (Throwable t) { + if (logger.isDebugEnabled()) { + logger.debug("Exception while fetching OAuth2 access token.", t); + } + throw t; + } + } +} \ No newline at end of file diff --git a/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/main/java/com/solacesystems/jcsmp/SolaceOAuth2SessionEventHandler.java b/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/main/java/com/solacesystems/jcsmp/SolaceOAuth2SessionEventHandler.java new file mode 100644 index 0000000..5ff0bff --- /dev/null +++ b/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/main/java/com/solacesystems/jcsmp/SolaceOAuth2SessionEventHandler.java @@ -0,0 +1,17 @@ +package com.solacesystems.jcsmp; + +/** + * The JCSMP {@link SessionEventHandler} when OAuth2 authentication scheme is being used. + * Implementing classes should handle the OAuth2 token refresh logic when the session is + * reconnecting. Refer {@link DefaultSolaceOAuth2SessionEventHandler} for the default + * implementation. + */ +public interface SolaceOAuth2SessionEventHandler extends SessionEventHandler { + + /** + * Sets the JCSMP session associated with this event handler. + * + * @param jcsmpSession The JCSMP session associated with this event handler. + */ + void setJcsmpSession(JCSMPSession jcsmpSession); +} \ No newline at end of file diff --git a/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/main/java/com/solacesystems/jcsmp/SolaceSessionOAuth2TokenProvider.java b/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/main/java/com/solacesystems/jcsmp/SolaceSessionOAuth2TokenProvider.java new file mode 100644 index 0000000..bd3f37e --- /dev/null +++ b/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/main/java/com/solacesystems/jcsmp/SolaceSessionOAuth2TokenProvider.java @@ -0,0 +1,16 @@ +package com.solacesystems.jcsmp; + +/** + * Interface for providing OAuth2 access tokens for Solace sessions. Implementing classes should + * provide a method to fetch and return the current OAuth2 access token. Refer + * {@link DefaultSolaceSessionOAuth2TokenProvider} for a default implementation. + */ +public interface SolaceSessionOAuth2TokenProvider { + + /** + * Fetches and returns the current OAuth2 access token. + * + * @return The current OAuth2 access token. + */ + String getAccessToken(); +} \ No newline at end of file diff --git a/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/main/java/com/solacesystems/jcsmp/SpringJCSMPFactory.java b/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/main/java/com/solacesystems/jcsmp/SpringJCSMPFactory.java index 25c8423..4d54b2c 100644 --- a/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/main/java/com/solacesystems/jcsmp/SpringJCSMPFactory.java +++ b/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/main/java/com/solacesystems/jcsmp/SpringJCSMPFactory.java @@ -19,88 +19,115 @@ package com.solacesystems.jcsmp; +import static com.solacesystems.jcsmp.JCSMPProperties.AUTHENTICATION_SCHEME; +import org.springframework.lang.Nullable; + /** - * Wrapper of JCSMP Singleton Factory to more easily work within Spring Auto Configuration environments. + * Wrapper of JCSMP Singleton Factory to more easily work within Spring Auto Configuration + * environments. */ public class SpringJCSMPFactory { - - protected JCSMPProperties jcsmpProperties; - public SpringJCSMPFactory(JCSMPProperties properties) { - this.jcsmpProperties = (JCSMPProperties)properties.clone(); - } + protected JCSMPProperties jcsmpProperties; + protected SolaceSessionOAuth2TokenProvider solaceSessionOAuth2TokenProvider; + public SpringJCSMPFactory(JCSMPProperties properties, + @Nullable SolaceSessionOAuth2TokenProvider solaceSessionOAuth2TokenProvider) { + this.jcsmpProperties = (JCSMPProperties) properties.clone(); + this.solaceSessionOAuth2TokenProvider = solaceSessionOAuth2TokenProvider; + } - /** - * Acquires a {@link JCSMPSession} implementation for the specified - * properties in the default Context. - * - * @return A {@link JCSMPSession} implementation with the specified - * properties. - * @throws InvalidPropertiesException - * Thrown if the required properties are not provided, or if - * unsupported properties (and combinations) are detected. - */ - public JCSMPSession createSession() throws InvalidPropertiesException { - return JCSMPFactory.onlyInstance().createSession(jcsmpProperties); - } - /** - * Acquires a {@link JCSMPSession} and associates it to the given - * {@link Context}. - * - * @param context - * The Context in which the new session will be - * created and associated with. If null, the - * default context is used. - * @return A newly constructed session in context. - * @throws InvalidPropertiesException - * on error - */ - public JCSMPSession createSession(Context context) throws InvalidPropertiesException { - return JCSMPFactory.onlyInstance().createSession(jcsmpProperties, context); - } + /** + * Acquires a {@link JCSMPSession} implementation for the specified properties in the default + * Context. + * + * @return A {@link JCSMPSession} implementation with the specified properties. + * @throws InvalidPropertiesException Thrown if the required properties are not provided, or if + * unsupported properties (and combinations) are detected. + */ + public JCSMPSession createSession() throws InvalidPropertiesException { + return createSession(null); + } - /** - * Acquires a {@link JCSMPSession} and associates it to the given - * {@link Context}. - * - * @param context - * The Context in which the new session will be - * created and associated with. If null, uses the - * default context. - * @param eventHandler - * A callback instance for handling session events. - * @return A newly constructed session in the context Context. - * @throws InvalidPropertiesException - * on error - */ - public JCSMPSession createSession( - Context context, - SessionEventHandler eventHandler) throws InvalidPropertiesException { - return JCSMPFactory.onlyInstance().createSession(jcsmpProperties, context, eventHandler); - } + /** + * Acquires a {@link JCSMPSession} and associates it to the given {@link Context}. + * + * @param context The Context in which the new session will be created and associated + * with. If null, the default context is used. + * @return A newly constructed session in context. + * @throws InvalidPropertiesException on error + */ + public JCSMPSession createSession(Context context) throws InvalidPropertiesException { + return createSession(context, null); + } - /* CONTEXT OPERATIONS */ - /** - * Returns a reference to the default Context. There is a - * single instance of a default context in the API. - * - * @return The default Context instance. - */ - public Context getDefaultContext() { - return JCSMPFactory.onlyInstance().getDefaultContext(); + /** + * Acquires a {@link JCSMPSession} and associates it to the given {@link Context}. + * If the authentication scheme is OAuth2, it fetches and sets the initial OAuth2 token. + * If the event handler is null, it creates a new session event handler that will handle OAuth2 token refreshes. + * + * @param context The Context in which the new session will be created and + * associated with. If null, uses the default context. + * @param eventHandler A callback instance for handling session events. + * @return A newly constructed session in the context Context. + * @throws InvalidPropertiesException on error + */ + public JCSMPSession createSession( + Context context, + SessionEventHandler eventHandler) throws InvalidPropertiesException { + final String authScheme = jcsmpProperties.getStringProperty(AUTHENTICATION_SCHEME); + if (JCSMPProperties.AUTHENTICATION_SCHEME_OAUTH2.equalsIgnoreCase(authScheme)) { + return createSessionWithOAuth2(context, eventHandler); + } else { + return JCSMPFactory.onlyInstance().createSession(jcsmpProperties, context, eventHandler); } + } - /** - * Creates a new Context with the provided properties. - * - * @param properties - * Configuration settings for the new Context. If - * null, the default configuration settings are used. - * @return Newly-created Context instance. - */ - public Context createContext(ContextProperties properties) { - return JCSMPFactory.onlyInstance().createContext(properties); + private JCSMPSession createSessionWithOAuth2(Context context, + SessionEventHandler eventHandler) throws InvalidPropertiesException { + if (eventHandler != null && !(eventHandler instanceof SolaceOAuth2SessionEventHandler)) { + throw new IllegalArgumentException(String.format( + "Event handler must be an instance of %s when using OAuth2 authentication scheme.", + SolaceOAuth2SessionEventHandler.class.getName())); } + + //A JCSMP SessionEventHandler, to handle OAuth2 token refreshes + final SolaceOAuth2SessionEventHandler solaceOAuth2SessionEventHandler = + eventHandler != null ? (SolaceOAuth2SessionEventHandler) eventHandler + : new DefaultSolaceOAuth2SessionEventHandler(this.jcsmpProperties, + this.solaceSessionOAuth2TokenProvider); + + //Fetch and set the initial OAuth2 token + final String accessToken = this.solaceSessionOAuth2TokenProvider.getAccessToken(); + this.jcsmpProperties.setProperty(JCSMPProperties.OAUTH2_ACCESS_TOKEN, accessToken); + + final JCSMPSession jcsmpSession = JCSMPFactory.onlyInstance() + .createSession(this.jcsmpProperties, context, solaceOAuth2SessionEventHandler); + //inject the JCSMP Session into the event handler + solaceOAuth2SessionEventHandler.setJcsmpSession(jcsmpSession); + return jcsmpSession; + } + + /* CONTEXT OPERATIONS */ + /** + * Returns a reference to the default Context. There is a single instance of a + * default context in the API. + * + * @return The default Context instance. + */ + public Context getDefaultContext() { + return JCSMPFactory.onlyInstance().getDefaultContext(); + } + + /** + * Creates a new Context with the provided properties. + * + * @param properties Configuration settings for the new Context. If + * null, the default configuration settings are used. + * @return Newly-created Context instance. + */ + public Context createContext(ContextProperties properties) { + return JCSMPFactory.onlyInstance().createContext(properties); + } } diff --git a/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/java/com/solace/it/util/semp/SempClientException.java b/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/java/com/solace/it/util/semp/SempClientException.java new file mode 100644 index 0000000..3a475da --- /dev/null +++ b/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/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-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/java/com/solace/it/util/semp/config/BrokerConfiguratorBuilder.java b/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/java/com/solace/it/util/semp/config/BrokerConfiguratorBuilder.java new file mode 100644 index 0000000..b85a406 --- /dev/null +++ b/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/java/com/solace/it/util/semp/config/BrokerConfiguratorBuilder.java @@ -0,0 +1,869 @@ +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.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.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 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-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/java/com/solace/it/util/semp/monitor/BrokerMonitorBuilder.java b/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/java/com/solace/it/util/semp/monitor/BrokerMonitorBuilder.java new file mode 100644 index 0000000..71e8dfb --- /dev/null +++ b/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/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-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/java/com/solace/spring/boot/autoconfigure/SolaceJavaAutoConfigurationTest.java b/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/java/com/solace/spring/boot/autoconfigure/SolaceJavaAutoConfigurationTest.java index 94cb427..f87644b 100644 --- a/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/java/com/solace/spring/boot/autoconfigure/SolaceJavaAutoConfigurationTest.java +++ b/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/java/com/solace/spring/boot/autoconfigure/SolaceJavaAutoConfigurationTest.java @@ -25,31 +25,28 @@ import com.solacesystems.jcsmp.JCSMPProperties; import com.solacesystems.jcsmp.JCSMPSession; import com.solacesystems.jcsmp.SpringJCSMPFactory; -import org.junit.After; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Test; import org.springframework.boot.test.util.TestPropertyValues; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Configuration; -import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; -@RunWith(SpringJUnit4ClassRunner.class) -public class SolaceJavaAutoConfigurationTest { +class SolaceJavaAutoConfigurationTest { @Configuration public static class EmptyConfiguration { } private AnnotationConfigApplicationContext context; private final Class configClass = SolaceJavaAutoConfiguration.class; - @After - public void tearDown() { + @AfterEach + void tearDown() { if (this.context != null) { this.context.close(); } } @Test - public void defaultNativeConnectionFactory() throws InvalidPropertiesException { + void defaultNativeConnectionFactory() throws InvalidPropertiesException { load(""); SpringJCSMPFactory jcsmpFactory = this.context.getBean(SpringJCSMPFactory.class); assertNotNull(jcsmpFactory); @@ -77,7 +74,7 @@ public void defaultNativeConnectionFactory() throws InvalidPropertiesException { } @Test - public void customNativeConnectionFactory() throws InvalidPropertiesException { + void customNativeConnectionFactory() throws InvalidPropertiesException { load("solace.java.host=192.168.1.80:55500", "solace.java.clientUsername=bob", "solace.java.clientPassword=password", "solace.java.msgVpn=newVpn", "solace.java.clientName=client-name", diff --git a/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/java/com/solace/spring/boot/autoconfigure/springBootTests/MessagingServiceFreeTierBrokerTestContainerWithTlsAndOAuthSetup.java b/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/java/com/solace/spring/boot/autoconfigure/springBootTests/MessagingServiceFreeTierBrokerTestContainerWithTlsAndOAuthSetup.java new file mode 100644 index 0000000..3585d71 --- /dev/null +++ b/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/java/com/solace/spring/boot/autoconfigure/springBootTests/MessagingServiceFreeTierBrokerTestContainerWithTlsAndOAuthSetup.java @@ -0,0 +1,117 @@ +package com.solace.spring.boot.autoconfigure.springBootTests; + +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/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/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-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/java/com/solace/spring/boot/autoconfigure/springBootTests/MessagingServiceFreeTierBrokerTestContainerWithTlsSetup.java b/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/java/com/solace/spring/boot/autoconfigure/springBootTests/MessagingServiceFreeTierBrokerTestContainerWithTlsSetup.java new file mode 100644 index 0000000..9f1d47c --- /dev/null +++ b/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/java/com/solace/spring/boot/autoconfigure/springBootTests/MessagingServiceFreeTierBrokerTestContainerWithTlsSetup.java @@ -0,0 +1,99 @@ +package com.solace.spring.boot.autoconfigure.springBootTests; + +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 MessagingServiceFreeTierBrokerTestContainerWithTlsSetup { + + String FULL_DOCKER_COMPOSE_FILE_PATH = "src/test/resources/free-tier-broker-with-tls-docker-compose.yml"; + String PUBSUB_BROKER_SERVICE_NAME = "solbroker"; + + Logger LOGGER = LoggerFactory.getLogger( + MessagingServiceFreeTierBrokerTestContainerWithTlsSetup.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) + + .waitingFor(PUBSUB_BROKER_SERVICE_NAME, + Wait.forHttp("/").forPort(8080).withStartupTimeout(Duration.ofSeconds(120))); + + @BeforeAll + static void startContainer() { + System.setProperty("javax.net.ssl.trustStore", + new File("src/test/resources/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"); + } + + @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", + "clientCertAuth_test_final_debug.log"); + containerState.copyFileFromContainer("/usr/sw/jail/logs/event.log", + "clientCertAuth_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-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/java/com/solace/spring/boot/autoconfigure/springBootTests/MessagingWithClientCertAuthIT.java b/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/java/com/solace/spring/boot/autoconfigure/springBootTests/MessagingWithClientCertAuthIT.java new file mode 100644 index 0000000..345bb6a --- /dev/null +++ b/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/java/com/solace/spring/boot/autoconfigure/springBootTests/MessagingWithClientCertAuthIT.java @@ -0,0 +1,109 @@ +package com.solace.spring.boot.autoconfigure.springBootTests; + +import static org.junit.jupiter.api.Assertions.fail; +import com.solace.it.util.semp.config.BrokerConfiguratorBuilder; +import com.solace.it.util.semp.config.BrokerConfiguratorBuilder.BrokerConfigurator; +import com.solace.test.integration.semp.v2.SempV2Api; +import com.solacesystems.jcsmp.JCSMPProperties; +import com.solacesystems.jcsmp.JCSMPSession; +import com.solacesystems.jcsmp.SpringJCSMPFactory; +import java.io.File; +import java.net.URISyntaxException; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import org.assertj.core.util.Files; +import org.junit.jupiter.api.BeforeAll; +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.context.ActiveProfiles; +import org.springframework.test.context.DynamicPropertyRegistry; +import org.springframework.test.context.DynamicPropertySource; + +@SpringBootTest( + classes = SampleApp.class, + webEnvironment = WebEnvironment.RANDOM_PORT +) +@AutoConfigureMockMvc() +@ActiveProfiles("clientCertAuthIT") +class MessagingWithClientCertAuthIT implements + MessagingServiceFreeTierBrokerTestContainerWithTlsSetup { + + private static final Logger logger = LoggerFactory.getLogger(MessagingWithClientCertAuthIT.class); + private static BrokerConfigurator solaceConfigUtil; + final static String MSG_VPN_DEFAULT = "default"; + + @Autowired + SpringJCSMPFactory springJCSMPFactory; + + @Autowired + JCSMPProperties jcsmpProperties; + + @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("solace.java.host", + () -> String.format("tcps://%s:%s", solaceHost, solaceSecureSMFPort)); + + registry.add("solace.java.apiProperties.SSL_TRUST_STORE", + () -> new File("src/test/resources/certs/client/client-truststore.p12").getAbsolutePath()); + registry.add("solace.java.apiProperties.SSL_KEY_STORE", + () -> new File("src/test/resources/certs/client/client-keystore.jks").getAbsolutePath()); + } + + @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 sempV2Api = new SempV2Api(sempUrl, "admin", "admin"); + solaceConfigUtil = BrokerConfiguratorBuilder.create(sempV2Api).build(); + + logger.debug("Prepare to upload CA cert to the broker"); + final URL resource = MessagingWithClientCertAuthIT.class.getClassLoader() + .getResource("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"); + } + + //Enable client certificate authentication on the Solace PubSub+ Broker + solaceConfigUtil.vpns().enableClientCertAuth(MSG_VPN_DEFAULT); + } catch (URISyntaxException e) { + fail(e); + } + } + + private boolean isClientCertificateAuthentication() { + return JCSMPProperties.AUTHENTICATION_SCHEME_CLIENT_CERTIFICATE.equalsIgnoreCase( + jcsmpProperties.getStringProperty(JCSMPProperties.AUTHENTICATION_SCHEME)); + } + + @Test + void canConnectWhenAuthenticationSchemeIsClientCertificate() { + if (!isClientCertificateAuthentication()) { + fail("Was expecting the authentication scheme to be client certificate."); + } + + try { + JCSMPSession jcsmpSession = springJCSMPFactory.createSession(); + jcsmpSession.connect(); + logger.info("Session connected successfully."); + jcsmpSession.closeSession(); + } catch (Exception e) { + fail(e); + } + } +} \ No newline at end of file diff --git a/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/java/com/solace/spring/boot/autoconfigure/springBootTests/MessagingWithOAuthIT.java b/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/java/com/solace/spring/boot/autoconfigure/springBootTests/MessagingWithOAuthIT.java new file mode 100644 index 0000000..47c3c90 --- /dev/null +++ b/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/java/com/solace/spring/boot/autoconfigure/springBootTests/MessagingWithOAuthIT.java @@ -0,0 +1,287 @@ +package com.solace.spring.boot.autoconfigure.springBootTests; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.fail; +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.config.model.ConfigMsgVpnAuthorizationGroup; +import com.solace.test.integration.semp.v2.monitor.model.MonitorMsgVpnClient; +import com.solacesystems.jcsmp.DefaultSolaceOAuth2SessionEventHandler; +import com.solacesystems.jcsmp.JCSMPProperties; +import com.solacesystems.jcsmp.JCSMPSession; +import com.solacesystems.jcsmp.SessionEventArgs; +import com.solacesystems.jcsmp.SessionEventHandler; +import com.solacesystems.jcsmp.SolaceSessionOAuth2TokenProvider; +import com.solacesystems.jcsmp.SpringJCSMPFactory; +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.Tag; +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.context.ActiveProfiles; +import org.springframework.test.context.DynamicPropertyRegistry; +import org.springframework.test.context.DynamicPropertySource; + +@SpringBootTest( + classes = SampleApp.class, + webEnvironment = WebEnvironment.RANDOM_PORT +) +@AutoConfigureMockMvc() +@ActiveProfiles("oauthIT") +class MessagingWithOAuthIT implements + MessagingServiceFreeTierBrokerTestContainerWithTlsAndOAuthSetup { + + private static final Logger logger = LoggerFactory.getLogger(MessagingWithOAuthIT.class); + private static BrokerConfigurator solaceConfigUtil; + private static BrokerMonitor solaceMonitorUtil; + private static SempV2Api sempV2Api; + + final static String OAUTH_PROFILE_NAME = "SolaceOauthResourceServer"; + final static String AUTHORIZATION_GROUP_NAME = "solclient_oauth_auth_group"; + final static String MSG_VPN_DEFAULT = "default"; + + @Autowired + SpringJCSMPFactory springJCSMPFactory; + + @Autowired + JCSMPProperties jcsmpProperties; + + @Autowired + SolaceSessionOAuth2TokenProvider solaceSessionOAuth2TokenProvider; + + @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("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.my-auth-server.token-uri", + () -> String.format( + "https://%s:%s/auth/realms/solace-oauth-resource-server-role/protocol/openid-connect/token", + nginxHost, nginxSecurePort)); + } + + @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(); + + logger.debug("Prepare to upload CA cert to the broker"); + final URL resource = MessagingWithOAuthIT.class.getClassLoader() + .getResource("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_DEFAULT); + } catch (URISyntaxException e) { + fail(e); + } + } + + private static void deleteOAuthSetup(String msgVpnName) { + solaceConfigUtil.vpns().disableOAuthAuth(msgVpnName); + solaceConfigUtil.vpns().deleteOAuthProfile(msgVpnName, OAUTH_PROFILE_NAME); + solaceConfigUtil.vpns().deleteAuthorizationGroup(msgVpnName, AUTHORIZATION_GROUP_NAME); + } + + private static void setupOAuth(String msgVpnName) { + solaceConfigUtil.vpns().enableOAuthAuth(msgVpnName); + solaceConfigUtil.vpns().createOAuthProfile(msgVpnName, oAuthProfileResourceServer()); + solaceConfigUtil.vpns().createAuthorizationGroup(msgVpnName, authorizationGroup1()); + } + + private static ConfigMsgVpnAuthenticationOauthProfile oAuthProfileResourceServer() { + final String AUTHORIZATION_GROUP_CLAIM_NAME = ""; + final String ENDPOINT_JWKS = "https://solaceoauth:10443/auth/realms/solace-oauth-resource-server-role/protocol/openid-connect/certs"; + final String ENDPOINT_USERINFO = "https://solaceoauth:10443/auth/realms/solace-oauth-resource-server-role/protocol/openid-connect/userinfo"; + final String REALM2_ISSUER_IDENTIFIER = "https://solaceoauth:10443/auth/realms/solace-oauth-resource-server-role"; + + 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); + } + + private static ConfigMsgVpnAuthorizationGroup authorizationGroup1() { + return new ConfigMsgVpnAuthorizationGroup() + .authorizationGroupName(AUTHORIZATION_GROUP_NAME) + .enabled(true) + .aclProfileName("default") + .clientProfileName("default"); + } + + private boolean isOAuth2() { + return JCSMPProperties.AUTHENTICATION_SCHEME_OAUTH2.equalsIgnoreCase( + jcsmpProperties.getStringProperty(JCSMPProperties.AUTHENTICATION_SCHEME)); + } + + @Test + void canConnectWhenAuthenticationSchemeIsOAuth2() { + if (!isOAuth2()) { + fail("Was expecting the authentication scheme to be OAuth2"); + } + + try { + JCSMPSession jcsmpSession = springJCSMPFactory.createSession(); + jcsmpSession.connect(); + logger.info("Session connected successfully."); + jcsmpSession.closeSession(); + } catch (Exception e) { + fail(e); + } + } + + + @Test + @Tag("SLOW") + void canRefreshTokenWhenAuthenticationSchemeIsOAuth2() { + if (!isOAuth2()) { + fail("Was expecting the authentication scheme to be OAuth2"); + } + + try { + CountDownLatch refreshedTokenLatch = new CountDownLatch(1); + SessionEventHandler sessionEventHandler = new DefaultSolaceOAuth2SessionEventHandler( + jcsmpProperties, solaceSessionOAuth2TokenProvider) { + @Override + public void handleEvent(SessionEventArgs sessionEventArgs) { + super.handleEvent(sessionEventArgs); + logger.info("Token refreshed successfully."); + refreshedTokenLatch.countDown(); + } + }; + + JCSMPSession jcsmpSession = springJCSMPFactory.createSession( + springJCSMPFactory.getDefaultContext(), sessionEventHandler); + jcsmpSession.connect(); + logger.info("Session connected successfully."); + logger.info("Wait for session reconnect, to refresh token. Will take about 1 minute."); + boolean success = refreshedTokenLatch.await(3, TimeUnit.MINUTES); + if (!success) { + fail("Timed out waiting for token refresh"); + } + jcsmpSession.closeSession(); + } catch (Exception e) { + fail(e); + } + } + + @Test + @Tag("SLOW") + void canRefreshTokenWhenForceReconnect() { + if (!isOAuth2()) { + fail("Was expecting the authentication scheme to be OAuth2"); + } + + try { + int numberOfReconnects = 10; + CountDownLatch refreshedTokenLatch = new CountDownLatch(numberOfReconnects); + SessionEventHandler sessionEventHandler = new DefaultSolaceOAuth2SessionEventHandler( + jcsmpProperties, solaceSessionOAuth2TokenProvider) { + @Override + public void handleEvent(SessionEventArgs sessionEventArgs) { + super.handleEvent(sessionEventArgs); + logger.info("Token refreshed successfully."); + refreshedTokenLatch.countDown(); + } + }; + + JCSMPSession jcsmpSession = springJCSMPFactory.createSession( + springJCSMPFactory.getDefaultContext(), sessionEventHandler); + jcsmpSession.connect(); + logger.info("Session connected successfully."); + + AtomicBoolean failed = new AtomicBoolean(false); + Thread t = new Thread(() -> { + for (int i = 0; i < numberOfReconnects; i++) { + try { + Thread.sleep(10_000); + MonitorMsgVpnClient msgVpnClient = solaceMonitorUtil.vpnClients() + .queryVpnClients(MSG_VPN_DEFAULT) + .stream() + .filter(client -> client.getClientUsername().startsWith("default")) + .findFirst().orElse(null); + + if (msgVpnClient == null) { + throw new RuntimeException("Client not found"); + } + + try { + logger.info("Forcing Session Reconnect for client: {}", msgVpnClient.getClientName()); + sempV2Api.action() + .doMsgVpnClientDisconnect(MSG_VPN_DEFAULT, msgVpnClient.getClientName(), + new ActionMsgVpnClientDisconnect()); + } catch (ApiException e) { + throw new RuntimeException(e); + } + } catch (Exception e) { + failed.set(true); + return; + } + } + }); + + t.start(); + + logger.info("Wait for session reconnect, to refresh token"); + boolean success = refreshedTokenLatch.await(3, TimeUnit.MINUTES); + if (!success) { + fail("Timed out waiting for token refresh"); + } + + jcsmpSession.closeSession(); + + assertFalse(failed.get(), "Failed to force reconnect"); + } catch (Exception e) { + fail(e); + } + } +} \ No newline at end of file diff --git a/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/java/com/solace/spring/boot/autoconfigure/springBootTests/SampleApp.java b/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/java/com/solace/spring/boot/autoconfigure/springBootTests/SampleApp.java new file mode 100644 index 0000000..2db89d4 --- /dev/null +++ b/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/java/com/solace/spring/boot/autoconfigure/springBootTests/SampleApp.java @@ -0,0 +1,14 @@ +package com.solace.spring.boot.autoconfigure.springBootTests; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; + +@SpringBootApplication +@EnableWebSecurity +public class SampleApp { + + public static void main(String[] args) { + SpringApplication.run(SampleApp.class, args); + } +} \ No newline at end of file diff --git a/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/java/com/solacesystems/jcsmp/DefaultSolaceOAuth2SessionEventHandlerTest.java b/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/java/com/solacesystems/jcsmp/DefaultSolaceOAuth2SessionEventHandlerTest.java new file mode 100644 index 0000000..bc8a786 --- /dev/null +++ b/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/java/com/solacesystems/jcsmp/DefaultSolaceOAuth2SessionEventHandlerTest.java @@ -0,0 +1,67 @@ +package com.solacesystems.jcsmp; + +import static com.solacesystems.jcsmp.JCSMPProperties.OAUTH2_ACCESS_TOKEN; +import static com.solacesystems.jcsmp.SessionEvent.DOWN_ERROR; +import static com.solacesystems.jcsmp.SessionEvent.RECONNECTING; +import static org.mockito.Mockito.anyString; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +public class DefaultSolaceOAuth2SessionEventHandlerTest { + + private SolaceSessionOAuth2TokenProvider mockTokenProvider; + private JCSMPSession mockSession; + + private DefaultSolaceOAuth2SessionEventHandler eventHandler; + + @BeforeEach + void setUp() { + mockTokenProvider = Mockito.mock(SolaceSessionOAuth2TokenProvider.class); + mockSession = Mockito.mock(JCSMPSession.class); + JCSMPProperties jcsmpProperties = new JCSMPProperties(); + jcsmpProperties.setProperty(JCSMPProperties.AUTHENTICATION_SCHEME, + JCSMPProperties.AUTHENTICATION_SCHEME_OAUTH2); + eventHandler = new DefaultSolaceOAuth2SessionEventHandler(jcsmpProperties, mockTokenProvider); + eventHandler.setJcsmpSession(mockSession); + } + + @Test + void shouldRefreshTokenOnReconnectingEvent() throws JCSMPException { + when(mockTokenProvider.getAccessToken()).thenReturn("newAccessToken"); + SessionEventArgs reconnecting = new SessionEventArgs(RECONNECTING, "Reconnecting", null, 0); + + eventHandler.handleEvent(reconnecting); + + verify(mockTokenProvider, times(1)).getAccessToken(); + verify(mockSession, times(1)).setProperty(OAUTH2_ACCESS_TOKEN, "newAccessToken"); + } + + @Test + void shouldNotRefreshTokenOnNonReconnectingEvent() throws JCSMPException { + SessionEventArgs downError = new SessionEventArgs(DOWN_ERROR, "DownError", null, 0); + + eventHandler.handleEvent(downError); + + verify(mockTokenProvider, never()).getAccessToken(); + verify(mockSession, never()).setProperty(eq(OAUTH2_ACCESS_TOKEN), anyString()); + } + + @Test + void shouldHandleExceptionWhenRefreshingToken() throws JCSMPException { + doThrow(new JCSMPException("Test exception")).when(mockSession) + .setProperty(eq(OAUTH2_ACCESS_TOKEN), anyString()); + SessionEventArgs reconnecting = new SessionEventArgs(RECONNECTING, "Reconnecting", null, 0); + + eventHandler.handleEvent(reconnecting); + + verify(mockTokenProvider, times(1)).getAccessToken(); + // No need to verify logging, just ensure no exception is thrown to the caller + } +} \ No newline at end of file diff --git a/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/application-clientCertAuthIT.yml b/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/application-clientCertAuthIT.yml new file mode 100644 index 0000000..f1c92ae --- /dev/null +++ b/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/application-clientCertAuthIT.yml @@ -0,0 +1,21 @@ +solace: + java: + host: tcps://localhost:55443 + msgVpn: default + clientUsername: default + clientPassword: ignored + connectRetries: 3 + reconnectRetries: 3 + connectRetriesPerHost: 1 + reconnectRetryWaitInMillis: 2000 + apiProperties: + SSL_VALIDATE_CERTIFICATE: true + SSL_VALIDATE_CERTIFICATE_DATE: true + SSL_VALIDATE_CERTIFICATE_HOST: true + AUTHENTICATION_SCHEME: AUTHENTICATION_SCHEME_CLIENT_CERTIFICATE + SSL_TRUST_STORE: certs/client/client-truststore.p12 #will be replaced by absolute path in test + SSL_TRUST_STORE_PASSWORD: changeMe123 + SSL_TRUST_STORE_FORMAT: PKCS12 + SSL_KEY_STORE: certs/client/client-keystore.jks #will be replaced by absolute path in test + SSL_KEY_STORE_PASSWORD: changeMe123 + SSL_KEY_STORE_FORMAT: JKS \ No newline at end of file diff --git a/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/application-oauthIT.yml b/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/application-oauthIT.yml new file mode 100644 index 0000000..1db77a7 --- /dev/null +++ b/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/application-oauthIT.yml @@ -0,0 +1,27 @@ +spring: + security: + oauth2: + client: + registration: + my-oauth2-client: + provider: my-auth-server + client-id: solclient_oauth + client-secret: j6gWnw13iqzJfFZzlqzaQabQgXza4oHl + authorization-grant-type: client_credentials + scope: openid + provider: + my-auth-server: + token-uri: https://localhost:10443/auth/realms/solace-oauth-resource-server-role/protocol/openid-connect/token + +solace: + java: + host: tcps://localhost:55443 + msgVpn: default + connectRetries: 3 + reconnectRetries: 3 + connectRetriesPerHost: 1 + reconnectRetryWaitInMillis: 2000 + oauth2ClientRegistrationId: my-oauth2-client + apiProperties: + SSL_VALIDATE_CERTIFICATE: false ## Because using self-signed certificate + AUTHENTICATION_SCHEME: AUTHENTICATION_SCHEME_OAUTH2 diff --git a/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/certs/README.txt b/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/certs/README.txt new file mode 100644 index 0000000..d1e517c --- /dev/null +++ b/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/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-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/certs/broker/solbroker.crt b/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/certs/broker/solbroker.crt new file mode 100644 index 0000000..70b2af5 --- /dev/null +++ b/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/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-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/certs/broker/solbroker.csr b/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/certs/broker/solbroker.csr new file mode 100644 index 0000000..3969f7c --- /dev/null +++ b/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/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-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/certs/broker/solbroker.key b/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/certs/broker/solbroker.key new file mode 100644 index 0000000..c2632ab --- /dev/null +++ b/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/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-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/certs/broker/solbroker.pem b/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/certs/broker/solbroker.pem new file mode 100644 index 0000000..21e12a1 --- /dev/null +++ b/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/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-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/certs/client/client-keystore.jks b/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/certs/client/client-keystore.jks new file mode 100644 index 0000000000000000000000000000000000000000..68b57de0087f508d0969a182afbfe92d9f256df7 GIT binary patch literal 2441 zcmb_d`8N~_8=lRW!HnIw%@^S+CdP~{t~Dx4F=LP=*)^6iqs%q7R49ZR8cUM3#mJJS z6tYB7$`FPS%4B5Xy4Ga--1~jsIrk6vet3U)o^zh_F6TMVyEDHt4*&o_dj>1Kg>!7H?dFlA#YyJ91$M3WIRNnayyTjBU`?PY1Qt^BjTDWx-H1r zi=#=EmF5`pCj56=$sq@IC?A!Cnz*l&8wxyuPTECv@O~`_gf1 z0-F?^AiCAxCIWj|+)5SC=m<6Kxna=l%@Do`6^8R%Rh ztB6J0Su7UEJbXya^h|$Um`kl|9voHd7P(zA`4~;Z6Nj*lymzkZ$lRT{C_&sLfzKHD4dfBIN%9u>?SmQEyk z@~)0geX=AeiWXsRNru&(mAyPM^1Y+7WWM&Rpt^f8ucSMSZEJ!IOsg5HFihl&zPup%C^d>5nbqNPD? z23dzdHmc`oYExEZj&)8iV6^i!RuwW;L)+}#8(3Rr7|MG@T!;4>?P~a9!&bxRsMfUL z{SIwNNf70 z{ODDolog)GMvs%fF8kHfR!CXG$CVmSBSCKeHwmYTezkYY51(niKNVkUrJLXFj!qr> zVdk28@3OP58BtZEx>NpVn;Dp8b+213c`i3;^PsJ1G*`2N0<|hp+Glcl$kJNy@aY2? zW>>f%w!k9Dv7_J|FBG#hDOT374=t`)WBnuk4(oD+c}xwRj8}#||2Y?Zf9F{EyYQQ< zZuI+5|1H;P?-H`m>06B5yB{M#e=fcydZ`Q}KICA(h)r!Kb6wVV2Hnl1~69TY!h zPqwcPJ6EC4U8 zm0WP`y;hKnRau3dOM4!M!K5Do-MK=1sbDn}`>=Y4X>7y|jP0NN;LE&eS$49m+oh8} zX4&Q>0B7s=OnS~NB6i~p3+2WlGR47j{=an(Kk*i9jew;}X*>v-XHFlFQ`RqC3D?RV zCumrkQ?7b;1g^J7zifjyr==D3`hV0pFPrm(huw#7xcfHh@zcL!QOFgmhcnzuL(9E1 zG{eP%4uNDSLc|=M_fx==uC)d%LIwxnfu6-7pm5AJ>uc)>AW&2YfIEX1#lfLqX9!dp zWO)=XfkXZUU}C}qD&6No5DhQBr`ThJ#h@pBs6KQbJPIfK7a<~sbfi&y`~nU*MupP@ zLc{;31&axjXf*l(%cD326hd1UcLa~a>*?re>$;;5x_c0(_g{!R`~Pb$g_HPeDzF$l zoJRT2i@?PLmH)XePzV5y2Z{ssco9%M5D4h_v(zKt*FA6Yc~%1=3yxMb$Se)|D1)s@ zq~$f(j%NvvpedKoZb8duSwUUQ80jL3mGK3nX;|y8>$pkPTtAex-Hbk=awmklyw*A% z_PdN{sLSmepTDOqmq!2bM5#X8z@mpZS{@-9$&p1%g{yc~GYX%5X_n?{kEW!!~TFEH=hVh;{-@;O`+7=JyZsPmbZejnya36eG0ZEOjL=f8AT-Ibku?wwB8mECOH zCv>`DB~_O_ZF!laKF11vgK3P@K;{U?CaR zW7iN>p@i~P$Ze^*B=Xj^Wh!OOy1;XmKNL2<{a79Ki^#!Sbr})FR}$@N)oOl{Kby9( zN9^7WsPgMzEGPM4iF3!anN)^*vf*Oy-JHxZ4Vz;X!>b5QPjQR=CZM~J0ny?0L6;66 znxOQ3Q2B+0Ubm)W31i=T%UVF*vNqL8Z%B_!aL8H2MSVA~d!UdlMuv^WQ%V@62{iTk zV6RxiOD+yNab;=J_PzIAO*!5M8)|rwkGzB+W&4y1Ejgdi>q{dksZpZ}lT}1NQHoLY zR2}p2Jja&0NRyZu@Q`6E^}*K|t4R3S*FDFPMX*t5MjqcTyi_GkUEVEw{{?$h+=qfl ziM5NwWba_#c5LBvzjD&W4}4s@J{HXpw!QaZ*+~BAq1Eyg5W1Ccy|S!kK<=jWGQE1t zU+1V{{k5DQ!WpqIe9uMTJeWl%vZCZU4v8M7k1lJpyB<@t5|h2}ZG`^CK-v4gb=`T} y_2Yc6`eiSTu0BegfjVgS#%{#z)68wI)*n*oO}Ij?xr=c{Vht~Ll)Y7TDF5H7%SG$} literal 0 HcmV?d00001 diff --git a/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/certs/client/client-truststore.p12 b/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/certs/client/client-truststore.p12 new file mode 100644 index 0000000000000000000000000000000000000000..e915ff1ea09720477b5e06419085fbdcce13178c GIT binary patch literal 1814 zcmV+x2kH1Qf(H@;0Ru3C2D}CdDuzgg_YDCD0ic2gtptJwsW5^Dr7(g9p#}*mhDe6@ z4FLxRpn?XHFoFh-0s#Opf(C~M2`Yw2hW8Bt2LUi<1_>&LNQUAwWg6_XiA`9Ige)zU@!s#ClCSwATSID2r7n1hW8Bu2?YQ! z9R>+thDZTr0|Wso1P~jmDE3MhL|LB}f+I8bpE`ho1`tEEW?^M+2@oi1hnvKl6swsX zt{fSBJoFU?9lwnhxlN(Qpk#m zh{de~;LVL+RND>z_*by?=Atny|?aUB6X>;_vU zxQAFeRRlBA2zO`Di?qS0jFz*Nl@M)JdFI!U zb8mmtZQ?hsOl=5 zqc3OZ>dgcM(rO*ywnbU#LV|DZLih7O2@Sv>T8(~IlC_K^0XvmfGM$A=Ge-}9xlO1C zpnvUcBfqt0GFitSvN7tIMPLxgm952B#ocF5 z8NbJd8H!2K*#7%{qj|g7F&JdrfzyxE9vI`Fin3b@N2^(r%6{9((kez@aApxNvF5G2 z*Xp{>zKO<0GPpK$(xZ5pV@ro%na zcU^)s`&iN2rP!=N@^MJXxAs3KrvJxHVMRn+^&eLJI-e6N98$mH*YtG`Xx7qs>0DWQ zw}$gS%rF3$M*|}!v?8)D+;h^i*X}25FP&?Ks@yr$gT-%_GzJgbkwAmXD;kzwSTF(& zVl}i>iEJgl8z^j7#U@M#6Yg>`U#G-y8*Z|!>L7;^pDy3Y*4w2><<*Dl*I30ue~FAQ zRu1P90ecW6^N*T%~09n1%-X3 zP&(k!D%sG{heJ!%U0tb-K@skFv8LIvwy?anYeJ2N7A z1fZGcu3YuE>ysh$paA{im%dTI#!G+&qZ{M$-Li=zzBJDE{*6Qs-GD+mh)*vWFl5VVeAxziYCRA0VGk3TV&4n(++hhASely6 z6n*%SKo`8$B--^R;25A=<#(V97uhq#b$%9z^h~{bd&v5>v>hLtt${4S)JRp{_s#(Q zoO2#kJC*Nwccr={&nF5#&*7MU>)#Kcsv_234|Z>?L2wR@yPOqml7zWgQNU}-9Ve@4 zGtrj_y;^SBR!!~O?pm=?s5Bc&TkECpjG0;O9+&AWUUPz5!~Cqyi}FK=*=A1a(yO5y zgA{AINU4F-JOT_JG;T;V_vYBT8oI*%_Xcz!edZw`91q1D1u=hOsxOnD*LhubzeyPA zm{6Nh&b}4CQm4&0NC9O71OfpC00bbt7bl%O zh*_%Sn)6p%-rtGMF8=~J)nDqHsIh+9u?)`y6m-!>m%8`?><86VTs%}8d9+|{{sICg E5a^y)OaK4? literal 0 HcmV?d00001 diff --git a/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/certs/client/client.crt b/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/certs/client/client.crt new file mode 100644 index 0000000..12c61d8 --- /dev/null +++ b/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/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-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/certs/client/client.csr b/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/certs/client/client.csr new file mode 100644 index 0000000..cb3ef9f --- /dev/null +++ b/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/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-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/certs/client/client.key b/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/certs/client/client.key new file mode 100644 index 0000000..6847c05 --- /dev/null +++ b/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/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-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/certs/client/client.p12 b/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/certs/client/client.p12 new file mode 100644 index 0000000000000000000000000000000000000000..52ca2b62bf7cba19c8dfccb06eaa3f693dc1c9ea GIT binary patch literal 2754 zcmY+^cQhM{9tZFsVucnlDx}n?(Gq(LV$a%p#cYd~S|u)G6}`1;#U7=_O|@vLRjUn3 zsfKWgQLPbDE)}EV^`7(IyYKz+JHK7NQzg#jMHF~9?QI>4{D^G`zu7X-2XdjU)bf^z_wbNqL4QAwrh z^z_VtL=5mPXNf*)+4FdeWVpS3Ty`?>F)Hlsu$mpN?4ru4U&(G%f#P3w*jsdkX2Hsa z?M6XPrXeIhM(&Wyc?HyKa#=MiB&dGYHkDBP={^F@oLMq6Mr@0}7V8ErkNlzZ`e?2I z?nUw~e3QL1l1sUr+Twpxz7j(YpS9p~Xg7Quk8*>5= zzFvCU>}f`8^5}r@DeX1EYUb*W3GPEkUDu)A9tX8@F1-%o&?Gt5sC)FA<0Z4}SkB-l z5jEHDyt?rHO0^>|M_!w%+K!ZU$~gC`CsJ0&j2g|^V8*XOLO8&wJOFH!f`oDGOn@lO zQQ$+^yT((cZ7=w5Ybu=g^&Y?2e-QXBLW$%yK%H_jz#!^cME0I**7mib0v65d6Cn0Q zV3U#{S69O6kaE@&B(@GZ@8M#z>um`RiVrtik&usp^Vr!f8=>{i7mWm#VxqMLi(kGV9v@o|>O?}xorV(3o(HRsQ6fnQa(I~Ums^J%4V6U(xkT3qRj= ziNawQcO3Tj?EMT7(&mBHr{PNpRx+F8Gs18i?>d8l*ugswa_*kX8wOkxJK$++b|5)B zXf1aRfCG6Wzdyx&5TZ|ggynM=Kc|woVCp4S1 zt&|z~)Y5ONAJ)l%j>!tUj7rWEU*8nywMp+Xv4ejzUi@7sIJ@hBKZZYq_cELKo`sq< zdb0A8K#f(tz2sSj5(1Ylp&%z)u{E)^4D34!|p(O`TmdQSW zL9#1btHfw2CIyR=`0x*|A;mW;>R83P*_mJVEmsh^5llh#<{xcU#=yPHmy|>NJrgt<(5PcjI;q$<6nmavHgcXRyz7$ zPEvjWx%Gc4GW@M#x!8U^8P}Tnw~9av5HIe#nahb7s9nHMWUDPafJ(l7$lwz~xGh}l zj7ro!g^~0neme?`m^2F?;&9OTyS90@grwGqLo*urXe?X?gXBynJ z)sG78`>|c7{c{PuLcvDQkVa|#PD3Am#9*H&oVWu$NA*81s<6j_Bn(&CW0?}gS?^u6 zM}>py2Ny(i8RmsAjH%Km5|$LrvhQlB8a+tkz_3x=i8^LTu3K?%>L~TYDtbhk?q2g* z(76Oopdt?P?J?3Ima~cb1SL9s1G;72)jV@@+k(w(bRtbVnC`K5cp%I4x_1TJKJ$i- z;Pakh_5{^;3T9WF=6TACcgEM&qb2w6d31EElv*AeG_Jm|u5={OTGRM_`XPrh!)HK) zI-=B=aj?tdj`rR9$~?88aZ&b-PnpIe-r>K4CTsWNRB>Ibj%6lQcg7o6n%sNDFV^}B>?#)K1>bbql)8!sw-|OR z_vjk(Z-G?_m>ppX)^NpuCj(Re-Ten2MS`?3OY>pf*%DeBaw?GU)mo2hL~61#>+9jS z`nI|9*M9cCVx(q~6+JR&2d@q!Efg7+@Juk%#C2@89;{o_y%E z9qnmSxdn-9^zfy;-WX+AYZ&|q(3JfP1$$PlN?ulu^gemN6U)d9#UTBX=8yAV%A1!G zw*tH=JX2`H>kJ_iE>$vMo!2j%JudhUYn5(I9a;)#e*;~Y$U2j-UBjO0`?nG1YM5k~ zw>z}~<*DhjcJ_*gBE;bkJNItyxB<%k!e@M&0*P@f-FD(DJ~V+Y{aUT<9CF#AFsBO# zj!N#A2_s%bo$$wNHOB&&@`@m0)PjmR_KM8)wwmfkUf4zFF5$L9^%}|G4Qw#9px6$T zr6JzWJ|Nsqh`S*9BQ5Mn<|W7B^rrW{N-=Y++I}n0Vf*RA8JDBD%9;?>>N{>if1Gfx z+8yF}-yqYg;`Mc0lt7>z)gTZAQTV*7yYN?k>>?MS}r`#>buo3U?c>3skEZ0x*VHW0GEf0 yz*#_yG8_!_0*nA4WYn~f#kGepxI4@9vR4Q?y~IHO6UeOWskGSI5t;Niq5lFYL>BV^ literal 0 HcmV?d00001 diff --git a/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/certs/client/client.pem b/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/certs/client/client.pem new file mode 100644 index 0000000..7d3d891 --- /dev/null +++ b/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/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-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/certs/keycloak/keycloak.crt b/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/certs/keycloak/keycloak.crt new file mode 100644 index 0000000..062f94b --- /dev/null +++ b/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/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-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/certs/keycloak/keycloak.csr b/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/certs/keycloak/keycloak.csr new file mode 100644 index 0000000..60032eb --- /dev/null +++ b/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/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-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/certs/keycloak/keycloak.key b/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/certs/keycloak/keycloak.key new file mode 100644 index 0000000..6d9102d --- /dev/null +++ b/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/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-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/certs/keycloak/keycloak.pem b/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/certs/keycloak/keycloak.pem new file mode 100644 index 0000000..d6020f3 --- /dev/null +++ b/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/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-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/certs/keycloak_san.conf b/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/certs/keycloak_san.conf new file mode 100644 index 0000000..be980a7 --- /dev/null +++ b/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/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-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/certs/rootCA/rootCA.crt b/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/certs/rootCA/rootCA.crt new file mode 100644 index 0000000..43fd900 --- /dev/null +++ b/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/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-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/certs/rootCA/rootCA.der b/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/certs/rootCA/rootCA.der new file mode 100644 index 0000000000000000000000000000000000000000..ca4a7fe1d46cd39da6f5736aaf1921c3ff23e15a GIT binary patch literal 1435 zcmXqLVx4Z##9Y6CnTe5!NhCPzk)!^7k@?bv&9Skq_cG=^+%n04myJ`a&7BGV`zp<>!|uI6E51iSrtn7?>Lx7@C-v8=FOm^O^v; z2BuIhZ7pwNQbP7DBP#=Q6B9p!K@$@fQxg*-!_-Sm2bZ;_N_Z8&Fu8Cq+&`^-_P4ZR zHnqpUf9uXMH(Q~5`HYTfDbx7}b%j;)ceq7X{7Z@Pn>E>h#ld&(7g4!84-(ToqHmQy z{(kpdQTClrTow9V_ivlqE;mx>dK7YY+mPFdi6W?;Ju?$?ue8nd$w}? ze|Jfd^Cs7=tr@SGA8Kztmn@K`%5>7QWxw++L+x;6&o*H?J;yMH(L zP2OZ-d*u?d^VPfdtm{qoYclQ5ki7E!?7|k=RN0hiCuWu_yzm#ETHGX%$h%kl)Xgu+ zCxzShubQc_?ZbAj1HVgO%~@Z#vWcbhqgn~8DaYDrU+(*SIxKZo!pF|KL8Ow?w0qj# zb)UGN9{VxF-^W4ccKk=FIXS12&Tv=#zT&wdsMSiV?8vgKyVQ1S)K~d-^@t^35A84RTKnUBpFfER?3So{{v#w_K5A}cHMivd$zb)a>aoFRh5SQ zk@t2xX6fx~H9hOTcm5&9%c{pz57qA8(34`5z+~^jBxg8LR_h&4@Y9mUWqFbutA`KEHFLG^0A1qh=_T03d)2(nlqI%`Q!?#+vP@gyZ#!;gQS&NBn-qFuq)sP zDG+94{LjK_zzn32lN~UR0h1jg!z8soH}rS!H~Y)7`i|qiZeQ~aPp_@~yKuqZ}RcK_qec zHpTC{n{xHo=f!V)&VE*PQ-|}@`@8=A|9EUeRc1=+QpU-@^S*A|7VG{i>6q-?w!;s; zCwy6~QpL&gVTZ3%KzE{2=h0FNp10&`oy#|Jxc~r|N@eE& literal 0 HcmV?d00001 diff --git a/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/certs/rootCA/rootCA.key b/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/certs/rootCA/rootCA.key new file mode 100644 index 0000000..58cb536 --- /dev/null +++ b/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/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-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/certs/rootCA/rootCA.pem b/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/certs/rootCA/rootCA.pem new file mode 100644 index 0000000..c43bc76 --- /dev/null +++ b/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/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-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/certs/rootCA/rootCA.srl b/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/certs/rootCA/rootCA.srl new file mode 100644 index 0000000..3ddaad8 --- /dev/null +++ b/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/certs/rootCA/rootCA.srl @@ -0,0 +1 @@ +6C05509733CDE0014B58B238944F91CF0D5C1BE9 diff --git a/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/certs/solbroker_san.conf b/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/certs/solbroker_san.conf new file mode 100644 index 0000000..37bf92f --- /dev/null +++ b/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/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-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/free-tier-broker-with-tls-and-oauth-docker-compose.yml b/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/free-tier-broker-with-tls-and-oauth-docker-compose.yml new file mode 100644 index 0000000..d5bca32 --- /dev/null +++ b/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/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-server-role-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-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/free-tier-broker-with-tls-docker-compose.yml b/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/free-tier-broker-with-tls-docker-compose.yml new file mode 100644 index 0000000..805d378 --- /dev/null +++ b/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/free-tier-broker-with-tls-docker-compose.yml @@ -0,0 +1,26 @@ +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 + +secrets: + server.pem: + file: "certs/broker/solbroker.pem" ## The server certificate for the Solace PubSub+ broker diff --git a/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/logback-test.xml b/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/logback-test.xml new file mode 100644 index 0000000..a9e0d03 --- /dev/null +++ b/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/logback-test.xml @@ -0,0 +1,15 @@ + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger -%msg%n%rEx{full, org} + + + + + + + + + + + \ No newline at end of file diff --git a/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/oauth/README b/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/oauth/README new file mode 100644 index 0000000..ae85175 --- /dev/null +++ b/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/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-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/oauth/keycloak/solace-oauth-resource-server-role-realm-export.json b/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/oauth/keycloak/solace-oauth-resource-server-role-realm-export.json new file mode 100644 index 0000000..3d2e063 --- /dev/null +++ b/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/oauth/keycloak/solace-oauth-resource-server-role-realm-export.json @@ -0,0 +1,2276 @@ +{ + "id": "solace-oauth-resource-server-role", + "realm": "solace-oauth-resource-server-role", + "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-server-role", + "description": "${role_default-roles}", + "composite": true, + "composites": { + "realm": [ + "offline_access", + "uma_authorization" + ], + "client": { + "account": [ + "manage-account", + "view-profile" + ] + } + }, + "clientRole": false, + "containerId": "solace-oauth-resource-server-role", + "attributes": {} + }, + { + "id": "2e5b3f5c-6bb7-4f35-94c3-a277948e2f06", + "name": "uma_authorization", + "description": "${role_uma_authorization}", + "composite": false, + "clientRole": false, + "containerId": "solace-oauth-resource-server-role", + "attributes": {} + }, + { + "id": "844b9405-65bf-4b12-8cd6-e88860d2afe1", + "name": "offline_access", + "description": "${role_offline-access}", + "composite": false, + "clientRole": false, + "containerId": "solace-oauth-resource-server-role", + "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-server-role", + "description": "${role_default-roles}", + "composite": true, + "clientRole": false, + "containerId": "solace-oauth-resource-server-role" + }, + "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-server-role" + ], + "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-server-role/account/", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "redirectUris": [ + "/realms/solace-oauth-resource-server-role/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-server-role/account/", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "redirectUris": [ + "/realms/solace-oauth-resource-server-role/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-server-role/console/", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "redirectUris": [ + "/admin/solace-oauth-resource-server-role/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-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/oauth/nginx.conf b/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/oauth/nginx.conf new file mode 100644 index 0000000..a3ba5e2 --- /dev/null +++ b/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/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-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/oauth/www/index.html b/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/oauth/www/index.html new file mode 100644 index 0000000..e6047aa --- /dev/null +++ b/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/oauth/www/index.html @@ -0,0 +1,6 @@ + + + Hello World!! + + + \ No newline at end of file diff --git a/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/solace.env b/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/solace.env new file mode 100644 index 0000000..04a0dfe --- /dev/null +++ b/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/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-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/solace_tls.env b/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/solace_tls.env new file mode 100644 index 0000000..cc90d8a --- /dev/null +++ b/solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/resources/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 diff --git a/solace-spring-boot-autoconfigure/solace-jms-spring-boot-autoconfigure/pom.xml b/solace-spring-boot-autoconfigure/solace-jms-spring-boot-autoconfigure/pom.xml index d0b9b70..224ce6c 100644 --- a/solace-spring-boot-autoconfigure/solace-jms-spring-boot-autoconfigure/pom.xml +++ b/solace-spring-boot-autoconfigure/solace-jms-spring-boot-autoconfigure/pom.xml @@ -5,12 +5,12 @@ com.solace.spring.boot solace-spring-boot-parent - 2.0.1-SNAPSHOT + 2.1.0-SNAPSHOT ../../solace-spring-boot-parent/pom.xml solace-jms-spring-boot-autoconfigure - 5.0.1-SNAPSHOT + 5.1.0-SNAPSHOT jar Solace Spring Boot Autoconfiguration - JMS @@ -47,9 +47,9 @@ test - com.github.stefanbirkner - system-rules - 1.19.0 + org.junit-pioneer + junit-pioneer + 1.9.1 test diff --git a/solace-spring-boot-autoconfigure/solace-jms-spring-boot-autoconfigure/src/test/java/com/solace/spring/boot/autoconfigure/SolaceJmsAutoConfigurationTest.java b/solace-spring-boot-autoconfigure/solace-jms-spring-boot-autoconfigure/src/test/java/com/solace/spring/boot/autoconfigure/SolaceJmsAutoConfigurationTest.java index 989821b..22b6c06 100644 --- a/solace-spring-boot-autoconfigure/solace-jms-spring-boot-autoconfigure/src/test/java/com/solace/spring/boot/autoconfigure/SolaceJmsAutoConfigurationTest.java +++ b/solace-spring-boot-autoconfigure/solace-jms-spring-boot-autoconfigure/src/test/java/com/solace/spring/boot/autoconfigure/SolaceJmsAutoConfigurationTest.java @@ -18,24 +18,24 @@ */ package com.solace.spring.boot.autoconfigure; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; import com.solacesystems.jms.SolConnectionFactoryImpl; -import org.junit.After; -import org.junit.Test; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Test; import org.springframework.boot.autoconfigure.jms.JmsAutoConfiguration; import org.springframework.boot.test.util.TestPropertyValues; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Configuration; import org.springframework.jms.core.JmsTemplate; -public class SolaceJmsAutoConfigurationTest { +class SolaceJmsAutoConfigurationTest { @Configuration public static class EmptyConfiguration {} private AnnotationConfigApplicationContext context; private final Class configClass = SolaceJmsAutoConfiguration.class; - @After + @AfterEach public void tearDown() { if (this.context != null) { this.context.close(); @@ -43,7 +43,7 @@ public void tearDown() { } @Test - public void defaultNativeConnectionFactory() { + void defaultNativeConnectionFactory() { load(""); JmsTemplate jmsTemplate = this.context.getBean(JmsTemplate.class); SolConnectionFactoryImpl connectionFactory = this.context @@ -57,7 +57,7 @@ public void defaultNativeConnectionFactory() { } @Test - public void customNativeConnectionFactory() { + void customNativeConnectionFactory() { load("solace.jms.host=192.168.1.80:55500", "solace.jms.clientUsername=bob", "solace.jms.clientPassword=password", "solace.jms.msgVpn=newVpn"); diff --git a/solace-spring-boot-bom/README.md b/solace-spring-boot-bom/README.md index c78b73a..8789590 100644 --- a/solace-spring-boot-bom/README.md +++ b/solace-spring-boot-bom/README.md @@ -21,6 +21,7 @@ Consult the table below to determine which version of the BOM you need to use: | 2.6.4 | 1.2.x | | 2.7.7 | 1.3.0 | | 3.0.6 | 2.0.0 | +| 3.3.1 | 2.1.0 | ## Including the BOM @@ -33,7 +34,7 @@ In addition to showing how to include the BOM, the following snippets also shows com.solace.spring.boot solace-spring-boot-bom - 2.0.0 + 2.1.0 pom import @@ -61,7 +62,7 @@ apply plugin: 'io.spring.dependency-management' dependencyManagement { imports { - mavenBom "com.solace.spring.boot:solace-spring-boot-bom:2.0.0" + mavenBom "com.solace.spring.boot:solace-spring-boot-bom:2.1.0" } } @@ -73,7 +74,7 @@ dependencies { ### Using it with Gradle 5 ```groovy dependencies { - implementation(platform("com.solace.spring.boot:solace-spring-boot-bom:2.0.0")) + implementation(platform("com.solace.spring.boot:solace-spring-boot-bom:2.1.0")) implementation("com.solace.spring.boot:solace-spring-boot-starter") } ``` diff --git a/solace-spring-boot-bom/pom.xml b/solace-spring-boot-bom/pom.xml index 55495e1..c6289f5 100644 --- a/solace-spring-boot-bom/pom.xml +++ b/solace-spring-boot-bom/pom.xml @@ -5,11 +5,11 @@ com.solace.spring.boot solace-spring-boot-build - 2.0.1-SNAPSHOT + 2.1.0-SNAPSHOT solace-spring-boot-bom - 2.0.1-SNAPSHOT + 2.1.0-SNAPSHOT pom Solace Spring Boot BOM diff --git a/solace-spring-boot-parent/pom.xml b/solace-spring-boot-parent/pom.xml index 3392d45..8073004 100644 --- a/solace-spring-boot-parent/pom.xml +++ b/solace-spring-boot-parent/pom.xml @@ -5,12 +5,12 @@ com.solace.spring.boot solace-spring-boot-build - 2.0.1-SNAPSHOT + 2.1.0-SNAPSHOT ../pom.xml solace-spring-boot-parent - 2.0.1-SNAPSHOT + 2.1.0-SNAPSHOT pom Solace Spring Boot Parent @@ -24,29 +24,18 @@ 17 17 - 5.0.1-SNAPSHOT - 5.0.1-SNAPSHOT + 5.1.0-SNAPSHOT + 5.1.0-SNAPSHOT - 10.20.0 - 10.20.0 + 10.22.0 + 10.22.0 - 2.12.4 + 2.22.2 + 2.22.2 - - - org.apache.logging.log4j - log4j-bom - 2.19.0 - pom - import - - org.springframework.boot @@ -73,16 +62,48 @@ - org.apache.maven.plugins maven-surefire-plugin ${surefire.plugin.version} --add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/java.util=ALL-UNNAMED + --add-opens java.base/java.net=ALL-UNNAMED + + + junit.jupiter.execution.parallel.enabled=false + + + + + org.apache.maven.plugins + maven-failsafe-plugin + ${failsafe.plugin.version} + + + integration-test + + integration-test + verify + + + + --add-opens java.base/java.lang=ALL-UNNAMED + --add-opens java.base/java.util=ALL-UNNAMED + --add-opens java.base/java.net=ALL-UNNAMED + + + + junit.jupiter.execution.parallel.enabled=false + + + + + + diff --git a/solace-spring-boot-samples/pom.xml b/solace-spring-boot-samples/pom.xml index 0c9ea29..60cf5ff 100644 --- a/solace-spring-boot-samples/pom.xml +++ b/solace-spring-boot-samples/pom.xml @@ -4,7 +4,7 @@ com.solace.spring.boot solace-spring-boot-samples - 2.0.1-SNAPSHOT + 2.1.0-SNAPSHOT pom Solace Spring Boot Samples Parent diff --git a/solace-spring-boot-samples/solace-java-oauth2-sample-app/README.md b/solace-spring-boot-samples/solace-java-oauth2-sample-app/README.md new file mode 100644 index 0000000..810cd01 --- /dev/null +++ b/solace-spring-boot-samples/solace-java-oauth2-sample-app/README.md @@ -0,0 +1,39 @@ +# Solace Spring Boot Sample - Java + +This is a simple sample project to demonstrate the autoconfiguration of the Solace Java API using the `solace-spring-boot-starter` (or the `solace-java-spring-boot-starter` in particular). + +Please refer to the [Spring Boot Auto-Configuration for the Solace Java API](../../solace-spring-boot-starters/solace-java-spring-boot-starter) project for more detail. + +## Contents + +* [Acquiring a Solace PubSub+ Service](#acquiring-a-solace-pubsub-service) +* [Configuring the Application to use your Solace PubSub+ Service Credentials](#configuring-the-sample-to-use-your-solace-pubsub-service-credentials) +* [Running the Sample](#running-the-sample) + +## Acquiring a Solace PubSub+ Service + +To run the samples you will need a Solace PubSub+ Event Broker. +Here are two ways to quickly get started if you don't already have a PubSub+ instance: + +1. Get a free Solace PubSub+ event broker cloud instance + * Visit https://solace.com/products/event-broker/cloud/ + * Create an account and instance for free +2. Run the Solace PubSub+ event broker locally + * Visit https://solace.com/downloads/ + * A variety of download options are available to run the software locally + * Follow the instructions for whatever download option you choose + +## Configuring the Sample to use your Solace PubSub+ Service Credentials + +Please consult the [Spring Boot Auto-Configuration for the Solace Java API](../../solace-spring-boot-starters/solace-java-spring-boot-starter/README.md#configure-the-application-to-use-your-solace-pubsub-service-credentials) documentation for the details on how to connect this sample to a Solace PubSub+ service. + +## Running the Sample + +The simplest way to run the sample is from the project root folder using maven. For example: + +```shell script +cd solace-spring-boot-samples/solace-java-oauth2-sample-app +mvn spring-boot:run +``` + +Hint: look for "Sending Hello World" and "TextMessage received: Hello World" in the displayed logs. \ No newline at end of file diff --git a/solace-spring-boot-samples/solace-java-oauth2-sample-app/pom.xml b/solace-spring-boot-samples/solace-java-oauth2-sample-app/pom.xml new file mode 100644 index 0000000..7e91c0d --- /dev/null +++ b/solace-spring-boot-samples/solace-java-oauth2-sample-app/pom.xml @@ -0,0 +1,105 @@ + + + 4.0.0 + + + com.solace.spring.boot + solace-spring-boot-samples + 2.1.0-SNAPSHOT + + + solace-java-oauth2-sample-app + 2.1.0-SNAPSHOT + jar + + Solace Spring Boot Sample - Java + Sample Application using the custom auto-config + https://github.com/SolaceProducts/solace-spring-boot + + + UTF-8 + demo.DemoApplication + 17 + 3.3.1 + 2.1.0-SNAPSHOT + + + + + + org.springframework.boot + spring-boot-dependencies + ${spring.boot.version} + pom + import + + + + com.solace.spring.boot + solace-spring-boot-bom + ${solace.spring.boot.version} + pom + import + + + + + + + com.solace.spring.boot + solace-spring-boot-starter + + + + org.springframework.boot + spring-boot-starter-oauth2-client + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.apache.maven.plugins + maven-deploy-plugin + 3.1.1 + + true + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.11.0 + + ${java.version} + ${java.version} + + + + org.springframework.boot + spring-boot-maven-plugin + ${spring.boot.version} + + true + false + + + + + repackage + + + + + + + diff --git a/solace-spring-boot-samples/solace-java-oauth2-sample-app/src/main/java/demo/DemoApplication.java b/solace-spring-boot-samples/solace-java-oauth2-sample-app/src/main/java/demo/DemoApplication.java new file mode 100644 index 0000000..442a3b4 --- /dev/null +++ b/solace-spring-boot-samples/solace-java-oauth2-sample-app/src/main/java/demo/DemoApplication.java @@ -0,0 +1,137 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package demo; + +import com.solacesystems.jcsmp.DeliveryMode; +import com.solacesystems.jcsmp.JCSMPFactory; +import com.solacesystems.jcsmp.JCSMPProperties; +import com.solacesystems.jcsmp.JCSMPSession; +import com.solacesystems.jcsmp.SpringJCSMPFactory; +import com.solacesystems.jcsmp.TextMessage; +import com.solacesystems.jcsmp.Topic; +import com.solacesystems.jcsmp.XMLMessageConsumer; +import com.solacesystems.jcsmp.XMLMessageProducer; +import java.security.SecureRandom; +import java.security.cert.X509Certificate; +import java.util.concurrent.TimeUnit; +import javax.net.ssl.HttpsURLConnection; +import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509TrustManager; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.CommandLineRunner; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.stereotype.Component; + +@SpringBootApplication +@EnableWebSecurity +public class DemoApplication { + + public static void main(String[] args) { + //In development environment, ignore certificates when using self-signed certs. This is not recommended for production + //Or alternatively, import the Solace broker's CA certificate into the Java Truststore + ignoreCertificates(); + SpringApplication.run(DemoApplication.class, args); + } + + @Component + static class Runner implements CommandLineRunner { + + private static final Logger logger = LoggerFactory.getLogger(Runner.class); + + private final Topic topic = JCSMPFactory.onlyInstance().createTopic("tutorial/topic"); + + @Autowired + private SpringJCSMPFactory solaceFactory; + + // Examples of other beans that can be used together to generate a customized SpringJCSMPFactory + @Autowired(required = false) + private JCSMPProperties jcsmpProperties; + + private DemoMessageConsumer msgConsumer = new DemoMessageConsumer(); + private DemoPublishEventHandler pubEventHandler = new DemoPublishEventHandler(); + + public void run(String... strings) throws Exception { + final String msg = "Hello World"; + final JCSMPSession session = solaceFactory.createSession(); + + XMLMessageConsumer cons = session.getMessageConsumer(msgConsumer); + + session.addSubscription(topic); + logger.info("Connected. Awaiting message..."); + cons.start(); + + // Consumer session is now hooked up and running! + + /** Anonymous inner-class for handling publishing events */ + XMLMessageProducer prod = session.getMessageProducer(pubEventHandler); + // Publish-only session is now hooked up and running! + + TextMessage jcsmpMsg = JCSMPFactory.onlyInstance().createMessage(TextMessage.class); + jcsmpMsg.setText(msg); + jcsmpMsg.setDeliveryMode(DeliveryMode.PERSISTENT); + + logger.info("============= Sending {}", msg); + prod.send(jcsmpMsg, topic); + + try { + // block here until message received, and latch will flip + msgConsumer.getLatch().await(10, TimeUnit.SECONDS); + } catch (InterruptedException e) { + logger.error("I was awoken while waiting"); + } + + Thread.sleep(600_000); + // Close consumer + cons.close(); + logger.info("Exiting."); + session.closeSession(); + } + } + + + private static void ignoreCertificates() { + TrustManager[] trustAllCerts = new TrustManager[]{new X509TrustManager() { + @Override + public X509Certificate[] getAcceptedIssuers() { + return new X509Certificate[]{}; + } + + @Override + public void checkClientTrusted(X509Certificate[] certs, String authType) { + } + + @Override + public void checkServerTrusted(X509Certificate[] certs, String authType) { + } + }}; + + try { + SSLContext sc = SSLContext.getInstance("TLS"); + sc.init(null, trustAllCerts, new SecureRandom()); + HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory()); + } catch (Exception e) { + e.printStackTrace(); + } + } +} \ No newline at end of file diff --git a/solace-spring-boot-samples/solace-java-oauth2-sample-app/src/main/java/demo/DemoMessageConsumer.java b/solace-spring-boot-samples/solace-java-oauth2-sample-app/src/main/java/demo/DemoMessageConsumer.java new file mode 100644 index 0000000..baac7e2 --- /dev/null +++ b/solace-spring-boot-samples/solace-java-oauth2-sample-app/src/main/java/demo/DemoMessageConsumer.java @@ -0,0 +1,54 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package demo; + +import java.util.concurrent.CountDownLatch; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.solacesystems.jcsmp.BytesXMLMessage; +import com.solacesystems.jcsmp.JCSMPException; +import com.solacesystems.jcsmp.TextMessage; +import com.solacesystems.jcsmp.XMLMessageListener; + +public class DemoMessageConsumer implements XMLMessageListener { + + private CountDownLatch latch = new CountDownLatch(1); + private static final Logger logger = LoggerFactory.getLogger(DemoMessageConsumer.class); + + public void onReceive(BytesXMLMessage msg) { + if (msg instanceof TextMessage) { + logger.info("============= TextMessage received: {}", ((TextMessage) msg).getText()); + } else { + logger.info("============= Message received."); + } + latch.countDown(); // unblock main thread + } + + public void onException(JCSMPException e) { + logger.info("Consumer received exception:", e); + latch.countDown(); // unblock main thread + } + + public CountDownLatch getLatch() { + return latch; + } + +} diff --git a/solace-spring-boot-samples/solace-java-oauth2-sample-app/src/main/java/demo/DemoPublishEventHandler.java b/solace-spring-boot-samples/solace-java-oauth2-sample-app/src/main/java/demo/DemoPublishEventHandler.java new file mode 100644 index 0000000..2d1b59f --- /dev/null +++ b/solace-spring-boot-samples/solace-java-oauth2-sample-app/src/main/java/demo/DemoPublishEventHandler.java @@ -0,0 +1,37 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package demo; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.solacesystems.jcsmp.JCSMPException; +import com.solacesystems.jcsmp.JCSMPStreamingPublishEventHandler; + +public class DemoPublishEventHandler implements JCSMPStreamingPublishEventHandler { + private static final Logger logger = LoggerFactory.getLogger(DemoPublishEventHandler.class); + + public void responseReceived(String messageID) { + logger.info("Producer received response for msg: {}", messageID); + } + + public void handleError(String messageID, JCSMPException e, long timestamp) { + logger.info("Producer received error for msg: {}@{} - {}%n", messageID, timestamp, e); + } +} diff --git a/solace-spring-boot-samples/solace-java-oauth2-sample-app/src/main/resources/application.yml b/solace-spring-boot-samples/solace-java-oauth2-sample-app/src/main/resources/application.yml new file mode 100644 index 0000000..6435377 --- /dev/null +++ b/solace-spring-boot-samples/solace-java-oauth2-sample-app/src/main/resources/application.yml @@ -0,0 +1,32 @@ +spring: + security: + oauth2: + client: + registration: + my-oauth2-client: + provider: my-auth-server + client-id: replace-client-id-here + client-secret: replace-client-secret-here + authorization-grant-type: client_credentials + #scope: optional-scopes + provider: + my-auth-server: + token-uri: replace-token-uri-here + +solace: + java: + host: tcps://localhost:55443 #Solace PubSub+ Broker secure connection URL + msgVpn: default + #clientUsername: not-required_will-be-ignored + #clientPassword: not-required_will-be-ignored + connectRetries: 3 + reconnectRetries: 3 + connectRetriesPerHost: 1 + reconnectRetryWaitInMillis: 3000 + oauth2ClientRegistrationId: my-oauth2-client # The registrationId of the OAuth2 client in the Spring Security configuration above + apiProperties: + #SSL_VALIDATE_CERTIFICATE: false # Set to false in local only, if using self-signed certificates + AUTHENTICATION_SCHEME: AUTHENTICATION_SCHEME_OAUTH2 # The authentication scheme to be used for connecting to the Solace PubSub+ Broker + +server: + port: 8090 \ No newline at end of file diff --git a/solace-spring-boot-samples/solace-java-sample-app/pom.xml b/solace-spring-boot-samples/solace-java-sample-app/pom.xml index 3075cfd..8edd15d 100644 --- a/solace-spring-boot-samples/solace-java-sample-app/pom.xml +++ b/solace-spring-boot-samples/solace-java-sample-app/pom.xml @@ -5,11 +5,11 @@ com.solace.spring.boot solace-spring-boot-samples - 2.0.1-SNAPSHOT + 2.1.0-SNAPSHOT solace-java-sample-app - 2.0.1-SNAPSHOT + 2.1.0-SNAPSHOT jar Solace Spring Boot Sample - Java @@ -20,24 +20,12 @@ UTF-8 demo.DemoApplication 17 - 3.0.6 - 2.0.1-SNAPSHOT + 3.3.1 + 2.1.0-SNAPSHOT - - - org.apache.logging.log4j - log4j-bom - 2.19.0 - pom - import - - org.springframework.boot spring-boot-dependencies diff --git a/solace-spring-boot-samples/solace-jms-sample-app-jndi/pom.xml b/solace-spring-boot-samples/solace-jms-sample-app-jndi/pom.xml index 62fee7b..6c9a5fb 100644 --- a/solace-spring-boot-samples/solace-jms-sample-app-jndi/pom.xml +++ b/solace-spring-boot-samples/solace-jms-sample-app-jndi/pom.xml @@ -5,11 +5,11 @@ com.solace.spring.boot solace-spring-boot-samples - 2.0.1-SNAPSHOT + 2.1.0-SNAPSHOT solace-jms-sample-app-jndi - 2.0.1-SNAPSHOT + 2.1.0-SNAPSHOT jar Solace Spring Boot Sample - JMS (JNDI) @@ -20,24 +20,12 @@ UTF-8 jndidemo.JndiDemoApplication 17 - 3.0.6 - 2.0.1-SNAPSHOT + 3.3.1 + 2.1.0-SNAPSHOT - - - org.apache.logging.log4j - log4j-bom - 2.19.0 - pom - import - - org.springframework.boot spring-boot-dependencies diff --git a/solace-spring-boot-samples/solace-jms-sample-app/pom.xml b/solace-spring-boot-samples/solace-jms-sample-app/pom.xml index af98062..872eb9d 100644 --- a/solace-spring-boot-samples/solace-jms-sample-app/pom.xml +++ b/solace-spring-boot-samples/solace-jms-sample-app/pom.xml @@ -5,11 +5,11 @@ com.solace.spring.boot solace-spring-boot-samples - 2.0.1-SNAPSHOT + 2.1.0-SNAPSHOT solace-jms-sample-app - 2.0.1-SNAPSHOT + 2.1.0-SNAPSHOT jar Solace Spring Boot Sample - JMS @@ -20,24 +20,12 @@ UTF-8 jmsdemo.DemoApplication 17 - 3.0.6 - 2.0.1-SNAPSHOT + 3.3.1 + 2.1.0-SNAPSHOT - - - org.apache.logging.log4j - log4j-bom - 2.19.0 - pom - import - - org.springframework.boot spring-boot-dependencies diff --git a/solace-spring-boot-starters/solace-java-spring-boot-starter/README.md b/solace-spring-boot-starters/solace-java-spring-boot-starter/README.md index ff824aa..1e66d33 100644 --- a/solace-spring-boot-starters/solace-java-spring-boot-starter/README.md +++ b/solace-spring-boot-starters/solace-java-spring-boot-starter/README.md @@ -6,6 +6,7 @@ This project provides Spring Boot Auto-Configuration and an associated Spring Bo * [Overview](#overview) * [Using Auto-Configuration in your App](#using-auto-configuration-in-your-app) +* [Using OAuth2 Authentication Scheme](#using-oauth2-authentication-scheme) * [Resources](#resources) --- @@ -39,7 +40,7 @@ Note that you'll need to include version 3.1.0 or later to use Spring Boot relea ```groovy // Solace Java API & auto-configuration -compile("com.solace.spring.boot:solace-java-spring-boot-starter:5.0.0") +compile("com.solace.spring.boot:solace-java-spring-boot-starter:5.1.0") ``` #### Using it with Maven @@ -49,7 +50,7 @@ compile("com.solace.spring.boot:solace-java-spring-boot-starter:5.0.0") com.solace.spring.boot solace-java-spring-boot-starter - 5.0.0 + 5.1.0 ``` @@ -92,6 +93,7 @@ solace.java.connectRetries solace.java.reconnectRetries solace.java.connectRetriesPerHost solace.java.reconnectRetryWaitInMillis +solace.java.oauth2ClientRegistrationId ##Set it when OAuth2 authentication scheme enabled. Reference to the Spring OAuth2 client registration-id. ``` Where reasonable, sensible defaults are always chosen. So a developer using a Solace PubSub+ message broker and wishing to use the default message-vpn may only set the `solace.java.host`. @@ -108,6 +110,102 @@ solace.java.apiProperties.client_channel_properties.keepAliveIntervalInMillis=30 Note that the direct configuration of `solace.java.` properties takes precedence over the `solace.java.apiProperties.`. +## Using OAuth2 Authentication Scheme + +This Spring Boot starter for Solace Java API supports OAuth2 authentication scheme. It requires a version of Solace PubSub+ broker that supports OAuth2 authentication scheme. + +The Solace PubSub+ Broker should be setup for OAuth2 authentication. Refer to +the [Solace PubSub+: Configuring-OAuth-Authorization](https://docs.solace.com/Security/Configuring-OAuth-Authorization.htm) +for more information. +See [Azure OAuth Setup](https://solace.com/blog/azure-oauth-setup-for-solace-rest-and-smf-clients/) +for example. + +You may also like to check +the [OAuth2 Integration Test](../../solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/test/java/com/solace/spring/boot/autoconfigure/springBootTests/MessagingWithOAuthIT.java) +for more information. + +> [!NOTE] +> The OAuth profile on Solace PubSub+ broker should be setup for Resource Server role. This Solace +> Java API Starer OAuth2 authentication scheme supports ```client_credentials``` grant type out-of-the +> box. + +> [!TIP] +> The OAuth2 grant type ```client_credentials``` is used for machine to machine authentication, it +> is recommended that Token expiry time is not too short as it may cause frequent token refreshes and +> impact the performance. + +### Using OAuth2 Authentication Scheme with Solace Java API + +To use OAuth2 authentication scheme with Solace Java API, follow these steps: + +Firstly, add the required dependencies to your `build.gradle` file: + +```groovy +compile("org.springframework.boot:spring-boot-starter-oauth2-client") +``` + +or `pom.xml` file: + +```xml + + + org.springframework.boot + spring-boot-starter-oauth2-client + +``` + +Secondly, add `@EnableWebSecurity` annotation to your Spring Boot application class: + +```java + +@SpringBootApplication +@EnableWebSecurity +public class DemoApplication { + +} +``` + +Finally, configure the Spring OAuth2 Client Registration provider through following properties in +your `application.properties` file: + +``` +##spring.security.oauth2.client.registration..provider= +spring.security.oauth2.client.registration.my-oauth2-client.provider=my-auth-server +spring.security.oauth2.client.registration.my-oauth2-client.client-id=replace-client-id-here +spring.security.oauth2.client.registration.my-oauth2-client.client-secret=replace-client-secret-here +spring.security.oauth2.client.registration.my-oauth2-client.authorization-grant-type=client_credentials ## only client_credentials grant type is supported + +##spring.security.oauth2.client.provider..token-uri= +spring.security.oauth2.client.provider.my-auth-server.token-uri=replace-token-uri-here + +solace.java.host=tcps://localhost:55443 ## OATUH2 authentication scheme requires a secure connection to the broker +solace.java.msgVpn=replace-msgVpn-here +solace.java.oauth2ClientRegistrationId=my-oauth2-client ## Refers to the Spring OAuth2 client registration-id defined above +solace.java.apiProperties.AUTHENTICATION_SCHEME=AUTHENTICATION_SCHEME_OAUTH2 +``` + +See +the [Solace Java API OAuth2 Sample](../../solace-spring-boot-samples/solace-java-oauth2-sample-app) +for an example of how to use OAuth2 authentication scheme. + +### Customizing OAuth2 Token Injection and Token Refresh + +The Solace Java API OAuth2 authentication scheme supports customizing the OAuth2 token injection and +token refresh. + +Create your custom implementation of +the [SolaceSessionOAuth2TokenProvider](../../solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/main/java/com/solacesystems/jcsmp/SolaceSessionOAuth2TokenProvider.java) +interface to injection initial token. +Refer [DefaultSolaceSessionOAuth2TokenProvider](../../solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/main/java/com/solacesystems/jcsmp/DefaultSolaceSessionOAuth2TokenProvider.java) +for sample implementation. + +Similarly, create your custom implementation of +the [SolaceOAuth2SessionEventHandler](../../solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/main/java/com/solacesystems/jcsmp/SolaceOAuth2SessionEventHandler.java) +interface to refresh token. +Refer [DefaultSolaceOAuth2SessionEventHandler](../../solace-spring-boot-autoconfigure/solace-java-spring-boot-autoconfigure/src/main/java/com/solacesystems/jcsmp/DefaultSolaceOAuth2SessionEventHandler.java) +for sample implementation. + + ## Resources For more information about Spring Boot Auto-Configuration and Starters try these resources: diff --git a/solace-spring-boot-starters/solace-java-spring-boot-starter/pom.xml b/solace-spring-boot-starters/solace-java-spring-boot-starter/pom.xml index ac3aca1..a735d48 100644 --- a/solace-spring-boot-starters/solace-java-spring-boot-starter/pom.xml +++ b/solace-spring-boot-starters/solace-java-spring-boot-starter/pom.xml @@ -5,12 +5,12 @@ com.solace.spring.boot solace-spring-boot-parent - 2.0.1-SNAPSHOT + 2.1.0-SNAPSHOT ../../solace-spring-boot-parent/pom.xml solace-java-spring-boot-starter - 5.0.1-SNAPSHOT + 5.1.0-SNAPSHOT jar Solace Spring Boot Starter - Java diff --git a/solace-spring-boot-starters/solace-jms-spring-boot-starter/README.md b/solace-spring-boot-starters/solace-jms-spring-boot-starter/README.md index 5a7b089..c37a492 100644 --- a/solace-spring-boot-starters/solace-jms-spring-boot-starter/README.md +++ b/solace-spring-boot-starters/solace-jms-spring-boot-starter/README.md @@ -52,7 +52,7 @@ Note that you'll need to include version 3.1.0 or later to use Spring Boot relea #### Using it with Gradle ```groovy -compile("com.solace.spring.boot:solace-jms-spring-boot-starter:5.0.0") +compile("com.solace.spring.boot:solace-jms-spring-boot-starter:5.1.0") ``` #### Using it with Maven @@ -61,7 +61,7 @@ compile("com.solace.spring.boot:solace-jms-spring-boot-starter:5.0.0") com.solace.spring.boot solace-jms-spring-boot-starter - 5.0.0 + 5.1.0 ``` diff --git a/solace-spring-boot-starters/solace-jms-spring-boot-starter/pom.xml b/solace-spring-boot-starters/solace-jms-spring-boot-starter/pom.xml index 99de113..4408d75 100644 --- a/solace-spring-boot-starters/solace-jms-spring-boot-starter/pom.xml +++ b/solace-spring-boot-starters/solace-jms-spring-boot-starter/pom.xml @@ -5,12 +5,12 @@ com.solace.spring.boot solace-spring-boot-parent - 2.0.1-SNAPSHOT + 2.1.0-SNAPSHOT ../../solace-spring-boot-parent/pom.xml solace-jms-spring-boot-starter - 5.0.1-SNAPSHOT + 5.1.0-SNAPSHOT jar Solace Spring Boot Starter - JMS diff --git a/solace-spring-boot-starters/solace-spring-boot-starter/pom.xml b/solace-spring-boot-starters/solace-spring-boot-starter/pom.xml index fe89f09..ed353f1 100644 --- a/solace-spring-boot-starters/solace-spring-boot-starter/pom.xml +++ b/solace-spring-boot-starters/solace-spring-boot-starter/pom.xml @@ -5,12 +5,12 @@ com.solace.spring.boot solace-spring-boot-parent - 2.0.1-SNAPSHOT + 2.1.0-SNAPSHOT ../../solace-spring-boot-parent/pom.xml solace-spring-boot-starter - 2.0.1-SNAPSHOT + 2.1.0-SNAPSHOT jar Solace Spring Boot Starter