From 0af8c35428839d5f06074a20b27fcade08f1d994 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Tue, 7 Jul 2020 13:07:00 +0100 Subject: [PATCH] Auto-configure Rabbit CF with credentials provider and refresh service Closes gh-22016 --- .../amqp/RabbitAutoConfiguration.java | 20 ++- .../amqp/RabbitAutoConfigurationTests.java | 138 ++++++++++++++++++ 2 files changed, 152 insertions(+), 6 deletions(-) diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/amqp/RabbitAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/amqp/RabbitAutoConfiguration.java index 065c7617bd..6276bf358c 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/amqp/RabbitAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/amqp/RabbitAutoConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2020 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,6 +20,8 @@ import java.time.Duration; import java.util.stream.Collectors; import com.rabbitmq.client.Channel; +import com.rabbitmq.client.impl.CredentialsProvider; +import com.rabbitmq.client.impl.CredentialsRefreshService; import org.springframework.amqp.core.AmqpAdmin; import org.springframework.amqp.rabbit.connection.CachingConnectionFactory; @@ -95,10 +97,13 @@ public class RabbitAutoConfiguration { @Bean public CachingConnectionFactory rabbitConnectionFactory(RabbitProperties properties, + ObjectProvider credentialsProvider, + ObjectProvider credentialsRefreshService, ObjectProvider connectionNameStrategy) throws Exception { - PropertyMapper map = PropertyMapper.get(); CachingConnectionFactory factory = new CachingConnectionFactory( - getRabbitConnectionFactoryBean(properties).getObject()); + getRabbitConnectionFactoryBean(properties, credentialsProvider, credentialsRefreshService) + .getObject()); + PropertyMapper map = PropertyMapper.get(); map.from(properties::determineAddresses).to(factory::setAddresses); map.from(properties::isPublisherReturns).to(factory::setPublisherReturns); map.from(properties::getPublisherConfirmType).whenNonNull().to(factory::setPublisherConfirmType); @@ -113,10 +118,11 @@ public class RabbitAutoConfiguration { return factory; } - private RabbitConnectionFactoryBean getRabbitConnectionFactoryBean(RabbitProperties properties) - throws Exception { - PropertyMapper map = PropertyMapper.get(); + private RabbitConnectionFactoryBean getRabbitConnectionFactoryBean(RabbitProperties properties, + ObjectProvider credentialsProvider, + ObjectProvider credentialsRefreshService) throws Exception { RabbitConnectionFactoryBean factory = new RabbitConnectionFactoryBean(); + PropertyMapper map = PropertyMapper.get(); map.from(properties::determineHost).whenNonNull().to(factory::setHost); map.from(properties::determinePort).to(factory::setPort); map.from(properties::determineUsername).whenNonNull().to(factory::setUsername); @@ -141,6 +147,8 @@ public class RabbitAutoConfiguration { } map.from(properties::getConnectionTimeout).whenNonNull().asInt(Duration::toMillis) .to(factory::setConnectionTimeout); + map.from(credentialsProvider::getIfUnique).whenNonNull().to(factory::setCredentialsProvider); + map.from(credentialsRefreshService::getIfUnique).whenNonNull().to(factory::setCredentialsRefreshService); factory.afterPropertiesSet(); return factory; } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/amqp/RabbitAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/amqp/RabbitAutoConfigurationTests.java index 00b79cffe9..4888109471 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/amqp/RabbitAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/amqp/RabbitAutoConfigurationTests.java @@ -28,6 +28,9 @@ import com.rabbitmq.client.Address; import com.rabbitmq.client.Connection; import com.rabbitmq.client.SslContextFactory; import com.rabbitmq.client.TrustEverythingTrustManager; +import com.rabbitmq.client.impl.CredentialsProvider; +import com.rabbitmq.client.impl.CredentialsRefreshService; +import com.rabbitmq.client.impl.DefaultCredentialsProvider; import org.aopalliance.aop.Advice; import org.junit.jupiter.api.Test; @@ -716,6 +719,49 @@ class RabbitAutoConfigurationTests { }); } + @Test + void whenACredentialsProviderIsAvailableThenConnectionFactoryIsConfiguredToUseIt() throws Exception { + this.contextRunner.withUserConfiguration(CredentialsProviderConfiguration.class) + .run((context) -> assertThat(getTargetConnectionFactory(context).params(null).getCredentialsProvider()) + .isEqualTo(CredentialsProviderConfiguration.credentialsProvider)); + } + + @Test + void whenAPrimaryCredentialsProviderIsAvailableThenConnectionFactoryIsConfiguredToUseIt() throws Exception { + this.contextRunner.withUserConfiguration(PrimaryCredentialsProviderConfiguration.class) + .run((context) -> assertThat(getTargetConnectionFactory(context).params(null).getCredentialsProvider()) + .isEqualTo(PrimaryCredentialsProviderConfiguration.credentialsProvider)); + } + + @Test + void whenMultipleCredentialsProvidersAreAvailableThenConnectionFactoryUsesDefaultProvider() throws Exception { + this.contextRunner.withUserConfiguration(MultipleCredentialsProvidersConfiguration.class) + .run((context) -> assertThat(getTargetConnectionFactory(context).params(null).getCredentialsProvider()) + .isInstanceOf(DefaultCredentialsProvider.class)); + } + + @Test + void whenACredentialsRefreshServiceIsAvailableThenConnectionFactoryIsConfiguredToUseIt() throws Exception { + this.contextRunner.withUserConfiguration(CredentialsRefreshServiceConfiguration.class).run( + (context) -> assertThat(getTargetConnectionFactory(context).params(null).getCredentialsRefreshService()) + .isEqualTo(CredentialsRefreshServiceConfiguration.credentialsRefreshService)); + } + + @Test + void whenAPrimaryCredentialsRefreshServiceIsAvailableThenConnectionFactoryIsConfiguredToUseIt() throws Exception { + this.contextRunner.withUserConfiguration(PrimaryCredentialsRefreshServiceConfiguration.class).run( + (context) -> assertThat(getTargetConnectionFactory(context).params(null).getCredentialsRefreshService()) + .isEqualTo(PrimaryCredentialsRefreshServiceConfiguration.credentialsRefreshService)); + } + + @Test + void whenMultipleCredentialsRefreshServiceAreAvailableThenConnectionFactoryHasNoCredentialsRefreshService() + throws Exception { + this.contextRunner.withUserConfiguration(MultipleCredentialsRefreshServicesConfiguration.class).run( + (context) -> assertThat(getTargetConnectionFactory(context).params(null).getCredentialsRefreshService()) + .isNull()); + } + private TrustManager getTrustManager(com.rabbitmq.client.ConnectionFactory rabbitConnectionFactory) { SslContextFactory sslContextFactory = (SslContextFactory) ReflectionTestUtils.getField(rabbitConnectionFactory, "sslContextFactory"); @@ -873,4 +919,96 @@ class RabbitAutoConfigurationTests { } + @Configuration(proxyBeanMethods = false) + static class CredentialsProviderConfiguration { + + private static final CredentialsProvider credentialsProvider = mock(CredentialsProvider.class); + + @Bean + CredentialsProvider credentialsProvider() { + return credentialsProvider; + } + + } + + @Configuration(proxyBeanMethods = false) + static class PrimaryCredentialsProviderConfiguration { + + private static final CredentialsProvider credentialsProvider = mock(CredentialsProvider.class); + + @Bean + @Primary + CredentialsProvider credentialsProvider() { + return credentialsProvider; + } + + @Bean + CredentialsProvider credentialsProvider1() { + return mock(CredentialsProvider.class); + } + + } + + @Configuration(proxyBeanMethods = false) + static class MultipleCredentialsProvidersConfiguration { + + @Bean + CredentialsProvider credentialsProvider1() { + return mock(CredentialsProvider.class); + } + + @Bean + CredentialsProvider credentialsProvider2() { + return mock(CredentialsProvider.class); + } + + } + + @Configuration(proxyBeanMethods = false) + static class CredentialsRefreshServiceConfiguration { + + private static final CredentialsRefreshService credentialsRefreshService = mock( + CredentialsRefreshService.class); + + @Bean + CredentialsRefreshService credentialsRefreshService() { + return credentialsRefreshService; + } + + } + + @Configuration(proxyBeanMethods = false) + static class PrimaryCredentialsRefreshServiceConfiguration { + + private static final CredentialsRefreshService credentialsRefreshService = mock( + CredentialsRefreshService.class); + + @Bean + @Primary + CredentialsRefreshService credentialsRefreshService1() { + return credentialsRefreshService; + } + + @Bean + CredentialsRefreshService credentialsRefreshService2() { + return mock(CredentialsRefreshService.class); + } + + } + + @Configuration(proxyBeanMethods = false) + static class MultipleCredentialsRefreshServicesConfiguration { + + @Bean + CredentialsRefreshService credentialsRefreshService1() { + return mock(CredentialsRefreshService.class); + } + + @Bean + CredentialsRefreshService credentialsRefreshService2() { + return mock(CredentialsRefreshService.class); + } + + } + }