From 0f42c5dd949e70c741b18d193aa20e7ff2d94d62 Mon Sep 17 00:00:00 2001 From: Dave Syer Date: Wed, 22 Feb 2017 11:29:14 +0000 Subject: [PATCH] Check there is only one ResourceServerConfiguration before modifying Fixes gh-8347 --- .../OAuth2ResourceServerConfiguration.java | 72 +++++------ ...ourceServerTokenServicesConfiguration.java | 2 +- ...tipleResourceServerConfigurationTests.java | 119 ++++++++++++++++++ 3 files changed, 154 insertions(+), 39 deletions(-) create mode 100644 spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/resource/MultipleResourceServerConfigurationTests.java diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/OAuth2ResourceServerConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/OAuth2ResourceServerConfiguration.java index 99a5d25fd6..df8acf86e3 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/OAuth2ResourceServerConfiguration.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/OAuth2ResourceServerConfiguration.java @@ -29,6 +29,8 @@ import org.springframework.boot.autoconfigure.condition.SpringBootCondition; import org.springframework.boot.autoconfigure.security.SecurityProperties; import org.springframework.boot.autoconfigure.security.oauth2.resource.OAuth2ResourceServerConfiguration.ResourceServerCondition; import org.springframework.boot.bind.RelaxedPropertyResolver; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Condition; import org.springframework.context.annotation.ConditionContext; @@ -84,8 +86,7 @@ public class OAuth2ResourceServerConfiguration { return new ResourceServerFilterChainOrderProcessor(properties); } - protected static class ResourceSecurityConfigurer - extends ResourceServerConfigurerAdapter { + protected static class ResourceSecurityConfigurer extends ResourceServerConfigurerAdapter { private ResourceServerProperties resource; @@ -94,8 +95,7 @@ public class OAuth2ResourceServerConfiguration { } @Override - public void configure(ResourceServerSecurityConfigurer resources) - throws Exception { + public void configure(ResourceServerSecurityConfigurer resources) throws Exception { resources.resourceId(this.resource.getResourceId()); } @@ -107,39 +107,42 @@ public class OAuth2ResourceServerConfiguration { } private static final class ResourceServerFilterChainOrderProcessor - implements BeanPostProcessor { + implements BeanPostProcessor, ApplicationContextAware { private final ResourceServerProperties properties; + private ApplicationContext context; - private ResourceServerFilterChainOrderProcessor( - ResourceServerProperties properties) { + private ResourceServerFilterChainOrderProcessor(ResourceServerProperties properties) { this.properties = properties; } @Override - public Object postProcessBeforeInitialization(Object bean, String beanName) - throws BeansException { + public void setApplicationContext(ApplicationContext context) throws BeansException { + this.context = context; + } + + @Override + public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { return bean; } @Override - public Object postProcessAfterInitialization(Object bean, String beanName) - throws BeansException { + public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if (bean instanceof ResourceServerConfiguration) { - ResourceServerConfiguration config = (ResourceServerConfiguration) bean; - config.setOrder(this.properties.getFilterOrder()); + if (this.context.getBeanNamesForType(ResourceServerConfiguration.class, false, false).length == 1) { + ResourceServerConfiguration config = (ResourceServerConfiguration) bean; + config.setOrder(this.properties.getFilterOrder()); + } } return bean; } } - protected static class ResourceServerCondition extends SpringBootCondition - implements ConfigurationCondition { + protected static class ResourceServerCondition extends SpringBootCondition implements ConfigurationCondition { private static final String AUTHORIZATION_ANNOTATION = "org.springframework." - + "security.oauth2.config.annotation.web.configuration." - + "AuthorizationServerEndpointsConfiguration"; + + "security.oauth2.config.annotation.web.configuration." + "AuthorizationServerEndpointsConfiguration"; @Override public ConfigurationPhase getConfigurationPhase() { @@ -147,43 +150,37 @@ public class OAuth2ResourceServerConfiguration { } @Override - public ConditionOutcome getMatchOutcome(ConditionContext context, - AnnotatedTypeMetadata metadata) { - ConditionMessage.Builder message = ConditionMessage - .forCondition("OAuth ResourceServer Condition"); + public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) { + ConditionMessage.Builder message = ConditionMessage.forCondition("OAuth ResourceServer Condition"); Environment environment = context.getEnvironment(); - RelaxedPropertyResolver resolver = new RelaxedPropertyResolver(environment, - "security.oauth2.resource."); + RelaxedPropertyResolver resolver = new RelaxedPropertyResolver(environment, "security.oauth2.resource."); if (hasOAuthClientId(environment)) { return ConditionOutcome.match(message.foundExactly("client-id property")); } if (!resolver.getSubProperties("jwt").isEmpty()) { - return ConditionOutcome - .match(message.foundExactly("JWT resource configuration")); + return ConditionOutcome.match(message.foundExactly("JWT resource configuration")); } if (!resolver.getSubProperties("jwk").isEmpty()) { return ConditionOutcome .match(message.foundExactly("JWK resource configuration")); } if (StringUtils.hasText(resolver.getProperty("user-info-uri"))) { - return ConditionOutcome - .match(message.foundExactly("user-info-uri property")); + return ConditionOutcome.match(message.foundExactly("user-info-uri property")); + } + if (StringUtils.hasText(resolver.getProperty("token-info-uri"))) { + return ConditionOutcome.match(message.foundExactly("token-info-uri property")); } if (ClassUtils.isPresent(AUTHORIZATION_ANNOTATION, null)) { - if (AuthorizationServerEndpointsConfigurationBeanCondition - .matches(context)) { - return ConditionOutcome.match( - message.found("class").items(AUTHORIZATION_ANNOTATION)); + if (AuthorizationServerEndpointsConfigurationBeanCondition.matches(context)) { + return ConditionOutcome.match(message.found("class").items(AUTHORIZATION_ANNOTATION)); } } - return ConditionOutcome.noMatch( - message.didNotFind("client id, JWT resource or authorization server") - .atAll()); + return ConditionOutcome + .noMatch(message.didNotFind("client id, JWT resource or authorization server").atAll()); } private boolean hasOAuthClientId(Environment environment) { - RelaxedPropertyResolver resolver = new RelaxedPropertyResolver(environment, - "security.oauth2.client."); + RelaxedPropertyResolver resolver = new RelaxedPropertyResolver(environment, "security.oauth2.client."); return StringUtils.hasLength(resolver.getProperty("client-id", "")); } @@ -194,8 +191,7 @@ public class OAuth2ResourceServerConfiguration { public static boolean matches(ConditionContext context) { Class type = AuthorizationServerEndpointsConfigurationBeanCondition.class; - Conditional conditional = AnnotationUtils.findAnnotation(type, - Conditional.class); + Conditional conditional = AnnotationUtils.findAnnotation(type, Conditional.class); StandardAnnotationMetadata metadata = new StandardAnnotationMetadata(type); for (Class conditionType : conditional.value()) { Condition condition = BeanUtils.instantiateClass(conditionType); diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/ResourceServerTokenServicesConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/ResourceServerTokenServicesConfiguration.java index 7b8cd849a5..47251080cb 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/ResourceServerTokenServicesConfiguration.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/ResourceServerTokenServicesConfiguration.java @@ -334,7 +334,7 @@ public class ResourceServerTokenServicesConfiguration { } String tokenInfoUri = resolver.getProperty("token-info-uri"); String userInfoUri = resolver.getProperty("user-info-uri"); - if (!StringUtils.hasLength(userInfoUri)) { + if (!StringUtils.hasLength(userInfoUri) && !StringUtils.hasLength(tokenInfoUri)) { return ConditionOutcome .match(message.didNotFind("user-info-uri property").atAll()); } diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/resource/MultipleResourceServerConfigurationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/resource/MultipleResourceServerConfigurationTests.java new file mode 100644 index 0000000000..32f3095cad --- /dev/null +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/resource/MultipleResourceServerConfigurationTests.java @@ -0,0 +1,119 @@ +/* + * Copyright 2012-2017 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.autoconfigure.security.oauth2.resource; + +import java.util.List; + +import org.junit.After; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration; +import org.springframework.boot.autoconfigure.security.oauth2.OAuth2AutoConfiguration; +import org.springframework.boot.autoconfigure.security.oauth2.OAuth2ClientProperties; +import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.boot.context.embedded.EmbeddedServletContainerFactory; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.boot.test.util.EnvironmentTestUtils; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import org.springframework.core.env.ConfigurableEnvironment; +import org.springframework.core.env.StandardEnvironment; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfiguration; +import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurer; +import org.springframework.security.oauth2.provider.token.RemoteTokenServices; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; + +/** + * @author Dave Syer + * + */ +public class MultipleResourceServerConfigurationTests { + + private ConfigurableApplicationContext context; + + private ConfigurableEnvironment environment = new StandardEnvironment(); + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + @After + public void close() { + if (this.context != null) { + this.context.close(); + } + } + + @Test + public void doubleResourceServerConfiguration() { + EnvironmentTestUtils.addEnvironment(this.environment, "debug=true", + "security.oauth2.resource.tokenInfoUri:http://example.com", "security.oauth2.client.clientId=acme"); + this.context = new SpringApplicationBuilder(DoubleResourceConfiguration.class, MockServletConfiguration.class) + .environment(this.environment).run(); + RemoteTokenServices services = this.context.getBean(RemoteTokenServices.class); + assertThat(services).isNotNull(); + } + + @Configuration + @Import({ OAuth2AutoConfiguration.class, PropertyPlaceholderAutoConfiguration.class }) + @EnableConfigurationProperties(OAuth2ClientProperties.class) + @EnableWebSecurity + protected static class MockServletConfiguration { + @Bean + public EmbeddedServletContainerFactory embeddedServletContainerFactory() { + return mock(EmbeddedServletContainerFactory.class); + } + } + + @Configuration + protected static class DoubleResourceConfiguration { + + @Bean + protected ResourceServerConfiguration adminResources() { + + ResourceServerConfiguration resource = new ResourceServerConfiguration() { + // Switch off the Spring Boot @Autowired configurers + public void setConfigurers(List configurers) { + super.setConfigurers(configurers); + } + }; + resource.setOrder(3); + return resource; + } + + @Bean + protected ResourceServerConfiguration otherResources() { + + ResourceServerConfiguration resource = new ResourceServerConfiguration() { + // Switch off the Spring Boot @Autowired configurers + public void setConfigurers(List configurers) { + super.setConfigurers(configurers); + } + }; + resource.setOrder(4); + return resource; + } + + } + +}