diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/ConditionOutcome.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/ConditionOutcome.java index a004c86067..af7c0894da 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/ConditionOutcome.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/ConditionOutcome.java @@ -102,4 +102,15 @@ public class ConditionOutcome { public String toString() { return (this.message == null ? "" : this.message); } + + /** + * Return the inverse of the specified condition outcome. + * @param outcome the outcome to inverse + * @return the inverse of the condition outcome + * @since 1.3.0 + */ + public static ConditionOutcome inverse(ConditionOutcome outcome) { + return new ConditionOutcome(!outcome.isMatch(), outcome.getMessage()); + } + } diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/SpringBootCondition.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/SpringBootCondition.java index c4ed041c2f..aa1dcb3e87 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/SpringBootCondition.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/SpringBootCondition.java @@ -18,16 +18,12 @@ package org.springframework.boot.autoconfigure.condition; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.springframework.beans.BeanUtils; import org.springframework.context.annotation.Condition; import org.springframework.context.annotation.ConditionContext; -import org.springframework.context.annotation.Conditional; -import org.springframework.core.annotation.AnnotationUtils; import org.springframework.core.type.AnnotatedTypeMetadata; import org.springframework.core.type.AnnotationMetadata; import org.springframework.core.type.ClassMetadata; import org.springframework.core.type.MethodMetadata; -import org.springframework.core.type.StandardAnnotationMetadata; import org.springframework.util.ClassUtils; import org.springframework.util.StringUtils; @@ -41,18 +37,6 @@ import org.springframework.util.StringUtils; public abstract class SpringBootCondition implements Condition { private final Log logger = LogFactory.getLog(getClass()); - - public static boolean evaluateForClass(Class annotated, ConditionContext context) { - Conditional conditional = AnnotationUtils.findAnnotation(annotated, Conditional.class); - StandardAnnotationMetadata metadata = new StandardAnnotationMetadata(annotated); - for (Class type : conditional.value()) { - Condition condition = BeanUtils.instantiateClass(type); - if (condition.matches(context, metadata)) { - return true; - } - } - return false; - } @Override public final boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/Http401AuthenticationEntryPoint.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/Http401AuthenticationEntryPoint.java index f8bbbccb31..571695c17b 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/Http401AuthenticationEntryPoint.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/Http401AuthenticationEntryPoint.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2014 the original author or authors. + * Copyright 2013-2015 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. @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.boot.autoconfigure.security; import java.io.IOException; @@ -26,26 +27,27 @@ import org.springframework.security.web.AuthenticationEntryPoint; import org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint; /** - * AuthenticationEntryPoint that sends a 401 and Parameterized by the value of the - * WWW-Authenticate header. Like the {@link BasicAuthenticationEntryPoint} but more + * AuthenticationEntryPoint that sends a 401 and Parameterized by the value of the {@coe + * WWW-Authenticate} header. Like the {@link BasicAuthenticationEntryPoint} but more * flexible. - * - * @author Dave Syer * + * @author Dave Syer + * @since 1.3.0 */ public class Http401AuthenticationEntryPoint implements AuthenticationEntryPoint { - private final String authenticateHeader; + private final String headerValue; - public Http401AuthenticationEntryPoint(String authenticateHeader) { - this.authenticateHeader = authenticateHeader; + public Http401AuthenticationEntryPoint(String headerValue) { + this.headerValue = headerValue; } @Override public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException { - response.setHeader("WWW-Authenticate", authenticateHeader); + response.setHeader("WWW-Authenticate", this.headerValue); response.sendError(HttpServletResponse.SC_UNAUTHORIZED, authException.getMessage()); } -} \ No newline at end of file + +} diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/OAuth2AutoConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/OAuth2AutoConfiguration.java index 203204d4c9..acdcd7ad76 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/OAuth2AutoConfiguration.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/OAuth2AutoConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2014 the original author or authors. + * Copyright 2012-2015 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,13 +20,14 @@ import org.springframework.beans.BeansException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.boot.autoconfigure.AutoConfigureBefore; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; import org.springframework.boot.autoconfigure.security.oauth2.authserver.SpringSecurityOAuth2AuthorizationServerConfiguration; import org.springframework.boot.autoconfigure.security.oauth2.client.OAuth2RestOperationsConfiguration; import org.springframework.boot.autoconfigure.security.oauth2.method.OAuth2MethodSecurityConfiguration; -import org.springframework.boot.autoconfigure.security.oauth2.resource.ResourceServerProperties; import org.springframework.boot.autoconfigure.security.oauth2.resource.OAuth2ResourceServerConfiguration; +import org.springframework.boot.autoconfigure.security.oauth2.resource.ResourceServerProperties; import org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; @@ -37,16 +38,16 @@ import org.springframework.security.oauth2.config.annotation.web.configuration.R import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; /** - * Spring Security OAuth2 top level auto-configuration beans + * {@link EnableAutoConfiguration Auto-configuration} for Spring Security OAuth2. * * @author Greg Turnquist * @author Dave Syer + * @since 1.3.0 */ @Configuration @ConditionalOnClass({ OAuth2AccessToken.class, WebMvcConfigurerAdapter.class }) @Import({ SpringSecurityOAuth2AuthorizationServerConfiguration.class, - OAuth2MethodSecurityConfiguration.class, - OAuth2ResourceServerConfiguration.class, + OAuth2MethodSecurityConfiguration.class, OAuth2ResourceServerConfiguration.class, OAuth2RestOperationsConfiguration.class }) @AutoConfigureBefore(WebMvcAutoConfiguration.class) @EnableConfigurationProperties(OAuth2ClientProperties.class) diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/OAuth2ClientProperties.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/OAuth2ClientProperties.java index b5682c4395..062666ece7 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/OAuth2ClientProperties.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/OAuth2ClientProperties.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2014 the original author or authors. + * Copyright 2012-2015 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. @@ -21,7 +21,10 @@ import java.util.UUID; import org.springframework.boot.context.properties.ConfigurationProperties; /** + * Configuration properties for OAuth2 Client. + * * @author Dave Syer + * @since 1.3.0 */ @ConfigurationProperties("spring.oauth2.client") public class OAuth2ClientProperties { diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/authserver/SpringSecurityOAuth2AuthorizationServerConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/authserver/SpringSecurityOAuth2AuthorizationServerConfiguration.java index 4a12ab036e..1796505376 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/authserver/SpringSecurityOAuth2AuthorizationServerConfiguration.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/authserver/SpringSecurityOAuth2AuthorizationServerConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2014 the original author or authors. + * Copyright 2012-2015 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. @@ -47,12 +47,13 @@ import org.springframework.security.oauth2.provider.client.BaseClientDetails; import org.springframework.security.oauth2.provider.token.TokenStore; /** - * Auto-configure a Spring Security OAuth2 authorization server. Back off if another + * Configuration for a Spring Security OAuth2 authorization server. Back off if another * {@link AuthorizationServerConfigurer} already exists or if authorization server is not * enabled. * * @author Greg Turnquist * @author Dave Syer + * @since 1.3.0 */ @Configuration @ConditionalOnClass(EnableAuthorizationServer.class) @@ -62,6 +63,9 @@ import org.springframework.security.oauth2.provider.token.TokenStore; public class SpringSecurityOAuth2AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter { + private static final Log logger = LogFactory + .getLog(SpringSecurityOAuth2AuthorizationServerConfiguration.class); + @Autowired private BaseClientDetails details; @@ -70,13 +74,10 @@ public class SpringSecurityOAuth2AuthorizationServerConfiguration extends @Autowired(required = false) private TokenStore tokenStore; - + @Configuration protected static class ClientDetailsLogger { - private static final Log logger = LogFactory - .getLog(SpringSecurityOAuth2AuthorizationServerConfiguration.class); - @Autowired private OAuth2ClientProperties credentials; diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/client/EnableOAuth2Sso.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/client/EnableOAuth2Sso.java index 04b255468e..bd98af729c 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/client/EnableOAuth2Sso.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/client/EnableOAuth2Sso.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 the original author or authors. + * Copyright 2012-2015 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. @@ -28,21 +28,22 @@ import org.springframework.security.config.annotation.web.configuration.WebSecur import org.springframework.security.oauth2.config.annotation.web.configuration.EnableOAuth2Client; /** - * Configuration for OAuth2 Single Sign On (SSO). If there is an existing + * Enable OAuth2 Single Sign On (SSO). If there is an existing * {@link WebSecurityConfigurerAdapter} provided by the user and annotated with - * @EnableOAuth2Sso, it is enhanced by adding an authentication filter and an - * authentication entry point. If the user only has @EnableOAuth2Sso but not - * on a WebSecurityConfigurerAdapter then one is added with all paths secured and with an - * order that puts it ahead of the default HTTP Basic security chain in Spring Boot. - * - * @author Dave Syer + * {@code @EnableOAuth2Sso}, it is enhanced by adding an authentication filter and an + * authentication entry point. If the user only has {@code @EnableOAuth2Sso} but not on a + * WebSecurityConfigurerAdapter then one is added with all paths secured and with an order + * that puts it ahead of the default HTTP Basic security chain in Spring Boot. * + * @author Dave Syer + * @since 1.3.0 */ @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @EnableOAuth2Client -@Import({ OAuth2SsoDefaultConfiguration.class, OAuth2SsoCustomConfiguration.class, ResourceServerTokenServicesConfiguration.class }) +@Import({ OAuth2SsoDefaultConfiguration.class, OAuth2SsoCustomConfiguration.class, + ResourceServerTokenServicesConfiguration.class }) public @interface EnableOAuth2Sso { } diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/client/OAuth2RestOperationsConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/client/OAuth2RestOperationsConfiguration.java index 744530be97..fd21d7daac 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/client/OAuth2RestOperationsConfiguration.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/client/OAuth2RestOperationsConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2014 the original author or authors. + * Copyright 2012-2015 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. @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.boot.autoconfigure.security.oauth2.client; import javax.annotation.Resource; @@ -49,8 +50,10 @@ import org.springframework.security.oauth2.provider.OAuth2Authentication; import org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationDetails; /** - * @author Dave Syer + * Configuration for OAuth2 Single Sign On REST operations. * + * @author Dave Syer + * @since 1.3.0 */ @Configuration @ConditionalOnClass(EnableOAuth2Client.class) @@ -109,7 +112,7 @@ public class OAuth2RestOperationsConfiguration { @Bean @Scope(value = "session", proxyMode = ScopedProxyMode.INTERFACES) public OAuth2ClientContext oauth2ClientContext() { - return new DefaultOAuth2ClientContext(accessTokenRequest); + return new DefaultOAuth2ClientContext(this.accessTokenRequest); } @Bean diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/client/OAuth2SsoCustomConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/client/OAuth2SsoCustomConfiguration.java index 5fea93f814..92dd7e3c4f 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/client/OAuth2SsoCustomConfiguration.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/client/OAuth2SsoCustomConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 the original author or authors. + * Copyright 2012-2015 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. @@ -44,9 +44,8 @@ import org.springframework.util.ReflectionUtils; * {@link WebSecurityConfigurerAdapter} provided by the user and annotated with * @EnableOAuth2Sso. The user-provided configuration is enhanced by adding an * authentication filter and an authentication entry point. - * - * @author Dave Syer * + * @author Dave Syer */ @Configuration @Conditional(WebSecurityEnhancerCondition.class) @@ -64,7 +63,8 @@ public class OAuth2SsoCustomConfiguration implements ImportAware, BeanPostProces @Override public void setImportMetadata(AnnotationMetadata importMetadata) { - configType = ClassUtils.resolveClassName(importMetadata.getClassName(), null); + this.configType = ClassUtils + .resolveClassName(importMetadata.getClassName(), null); } @@ -77,11 +77,11 @@ public class OAuth2SsoCustomConfiguration implements ImportAware, BeanPostProces @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { - if (configType.isAssignableFrom(bean.getClass()) + if (this.configType.isAssignableFrom(bean.getClass()) && bean instanceof WebSecurityConfigurerAdapter) { ProxyFactory factory = new ProxyFactory(); factory.setTarget(bean); - factory.addAdvice(new SsoSecurityAdapter(beanFactory)); + factory.addAdvice(new SsoSecurityAdapter(this.beanFactory)); bean = factory.getProxy(); } return bean; @@ -92,7 +92,7 @@ public class OAuth2SsoCustomConfiguration implements ImportAware, BeanPostProces private SsoSecurityConfigurer configurer; public SsoSecurityAdapter(BeanFactory beanFactory) { - configurer = new SsoSecurityConfigurer(beanFactory); + this.configurer = new SsoSecurityConfigurer(beanFactory); } @Override @@ -102,8 +102,8 @@ public class OAuth2SsoCustomConfiguration implements ImportAware, BeanPostProces WebSecurityConfigurerAdapter.class, "getHttp"); ReflectionUtils.makeAccessible(method); HttpSecurity http = (HttpSecurity) ReflectionUtils.invokeMethod(method, - (WebSecurityConfigurerAdapter) invocation.getThis()); - configurer.configure(http); + invocation.getThis()); + this.configurer.configure(http); } return invocation.proceed(); } @@ -127,6 +127,7 @@ public class OAuth2SsoCustomConfiguration implements ImportAware, BeanPostProces return ConditionOutcome .noMatch("found no @EnableOAuth2Sso on a WebSecurityConfigurerAdapter"); } + } } diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/client/OAuth2SsoDefaultConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/client/OAuth2SsoDefaultConfiguration.java index 6445a074d3..d8d509de66 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/client/OAuth2SsoDefaultConfiguration.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/client/OAuth2SsoDefaultConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 the original author or authors. + * Copyright 2012-2015 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. @@ -32,12 +32,13 @@ import org.springframework.security.config.annotation.web.configuration.WebSecur import org.springframework.util.ClassUtils; /** - * If the user only has @EnableOAuth2Sso but not on a - * WebSecurityConfigurerAdapter then one is added with all paths secured and with an order - * that puts it ahead of the default HTTP Basic security chain in Spring Boot. - * - * @author Dave Syer + * Configuration for OAuth2 Single Sign On (SSO). If the user only has + * {@code @EnableOAuth2Sso} but not on a {@code WebSecurityConfigurerAdapter} then one is + * added with all paths secured and with an order that puts it ahead of the default HTTP + * Basic security chain in Spring Boot. * + * @author Dave Syer + * @since 1.3.0 */ @Configuration @EnableConfigurationProperties(OAuth2SsoProperties.class) @@ -57,13 +58,13 @@ public class OAuth2SsoDefaultConfiguration { @Override protected void configure(HttpSecurity http) throws Exception { http.antMatcher("/**").authorizeRequests().anyRequest().authenticated(); - new SsoSecurityConfigurer(beanFactory).configure(http); + new SsoSecurityConfigurer(this.beanFactory).configure(http); } @Override public int getOrder() { - if (sso.getFilterOrder() != null) { - return sso.getFilterOrder(); + if (this.sso.getFilterOrder() != null) { + return this.sso.getFilterOrder(); } if (ClassUtils .isPresent( @@ -80,6 +81,7 @@ public class OAuth2SsoDefaultConfiguration { } private static class NeedsWebSecurityCondition extends SpringBootCondition { + @Override public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) { @@ -95,6 +97,7 @@ public class OAuth2SsoDefaultConfiguration { return ConditionOutcome .match("found no @EnableOAuth2Sso on a WebSecurityConfigurerAdapter"); } + } } diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/client/OAuth2SsoProperties.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/client/OAuth2SsoProperties.java index d666fc22e8..ad3762ee5b 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/client/OAuth2SsoProperties.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/client/OAuth2SsoProperties.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2014 the original author or authors. + * Copyright 2012-2015 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. @@ -13,13 +13,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.boot.autoconfigure.security.oauth2.client; import org.springframework.boot.context.properties.ConfigurationProperties; /** - * @author Dave Syer + * Configuration properties for OAuth2 Single Sign On (SSO). * + * @author Dave Syer + * @since 1.3.0 */ @ConfigurationProperties("spring.oauth2.sso") public class OAuth2SsoProperties { @@ -39,7 +42,7 @@ public class OAuth2SsoProperties { private Integer filterOrder; public String getLoginPath() { - return loginPath; + return this.loginPath; } public void setLoginPath(String loginPath) { @@ -47,7 +50,7 @@ public class OAuth2SsoProperties { } public Integer getFilterOrder() { - return filterOrder; + return this.filterOrder; } public void setFilterOrder(Integer filterOrder) { diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/client/SsoSecurityConfigurer.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/client/SsoSecurityConfigurer.java index 4e5fe1515f..5c44a39c16 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/client/SsoSecurityConfigurer.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/client/SsoSecurityConfigurer.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 the original author or authors. + * Copyright 2012-2015 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. @@ -36,7 +36,7 @@ class SsoSecurityConfigurer { } public void configure(HttpSecurity http) throws Exception { - OAuth2SsoProperties sso = beanFactory.getBean(OAuth2SsoProperties.class); + OAuth2SsoProperties sso = this.beanFactory.getBean(OAuth2SsoProperties.class); // Delay the processing of the filter until we know the // SessionAuthenticationStrategy is available: http.apply(new OAuth2ClientAuthenticationConfigurer(oauth2SsoFilter(sso))); @@ -46,9 +46,9 @@ class SsoSecurityConfigurer { private OAuth2ClientAuthenticationProcessingFilter oauth2SsoFilter( OAuth2SsoProperties sso) { - OAuth2RestOperations restTemplate = beanFactory + OAuth2RestOperations restTemplate = this.beanFactory .getBean(OAuth2RestOperations.class); - ResourceServerTokenServices tokenServices = beanFactory + ResourceServerTokenServices tokenServices = this.beanFactory .getBean(ResourceServerTokenServices.class); OAuth2ClientAuthenticationProcessingFilter filter = new OAuth2ClientAuthenticationProcessingFilter( sso.getLoginPath()); @@ -59,6 +59,7 @@ class SsoSecurityConfigurer { private static class OAuth2ClientAuthenticationConfigurer extends SecurityConfigurerAdapter { + private OAuth2ClientAuthenticationProcessingFilter filter; public OAuth2ClientAuthenticationConfigurer( @@ -68,12 +69,13 @@ class SsoSecurityConfigurer { @Override public void configure(HttpSecurity builder) throws Exception { - OAuth2ClientAuthenticationProcessingFilter ssoFilter = filter; + OAuth2ClientAuthenticationProcessingFilter ssoFilter = this.filter; ssoFilter.setSessionAuthenticationStrategy(builder .getSharedObject(SessionAuthenticationStrategy.class)); builder.addFilterAfter(ssoFilter, AbstractPreAuthenticatedProcessingFilter.class); } + } -} \ No newline at end of file +} diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/method/OAuth2MethodSecurityConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/method/OAuth2MethodSecurityConfiguration.java index d9b5b58ca7..9d81f39cba 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/method/OAuth2MethodSecurityConfiguration.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/method/OAuth2MethodSecurityConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2014 the original author or authors. + * Copyright 2012-2015 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. @@ -30,22 +30,22 @@ import org.springframework.security.oauth2.provider.expression.OAuth2MethodSecur /** * Auto-configure an expression handler for method-level security (if the user already has - * @EnableGlobalMethodSecurity). + * {@code @EnableGlobalMethodSecurity}). * * @author Greg Turnquist * @author Dave Syer + * @since 1.3.0 */ @Configuration @ConditionalOnClass({ OAuth2AccessToken.class }) @ConditionalOnBean(GlobalMethodSecurityConfiguration.class) -public class OAuth2MethodSecurityConfiguration implements - BeanFactoryPostProcessor { +public class OAuth2MethodSecurityConfiguration implements BeanFactoryPostProcessor { @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { - beanFactory - .addBeanPostProcessor(new OAuth2ExpressionHandlerInjectionPostProcessor()); + OAuth2ExpressionHandlerInjectionPostProcessor processor = new OAuth2ExpressionHandlerInjectionPostProcessor(); + beanFactory.addBeanPostProcessor(processor); } private static class OAuth2ExpressionHandlerInjectionPostProcessor implements diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/JwtAccessTokenConverterConfigurer.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/JwtAccessTokenConverterConfigurer.java index 2ebf2d7ce6..2e43ce43d4 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/JwtAccessTokenConverterConfigurer.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/JwtAccessTokenConverterConfigurer.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2015 the original author or authors. + * Copyright 2012-2015 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. @@ -13,12 +13,24 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.boot.autoconfigure.security.oauth2.resource; import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter; +/** + * Callback interface that can be used to provide additional configuration to the + * {@link JwtAccessTokenConverter}. + * + * @author Dave Syer + * @since 1.3.0 + */ public interface JwtAccessTokenConverterConfigurer { + /** + * Configure the {@link JwtAccessTokenConverter}. + * @param converter the converter to configure + */ void configure(JwtAccessTokenConverter converter); } 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 c70c0d92ab..156fac02f1 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 @@ -1,5 +1,5 @@ /* - * Copyright 2012-2014 the original author or authors. + * Copyright 2012-2015 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. @@ -16,6 +16,7 @@ package org.springframework.boot.autoconfigure.security.oauth2.resource; +import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionOutcome; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; @@ -27,13 +28,16 @@ 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.annotation.Bean; +import org.springframework.context.annotation.Condition; import org.springframework.context.annotation.ConditionContext; import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.ConfigurationCondition; import org.springframework.context.annotation.Import; +import org.springframework.core.annotation.AnnotationUtils; import org.springframework.core.env.Environment; import org.springframework.core.type.AnnotatedTypeMetadata; +import org.springframework.core.type.StandardAnnotationMetadata; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerEndpointsConfiguration; import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer; @@ -50,6 +54,7 @@ import org.springframework.util.StringUtils; * * @author Greg Turnquist * @author Dave Syer + * @since 1.3.0 */ @Configuration @Conditional(ResourceServerCondition.class) @@ -91,10 +96,13 @@ public class OAuth2ResourceServerConfiguration { } - @ConditionalOnBean(AuthorizationServerEndpointsConfiguration.class) protected static class ResourceServerCondition extends SpringBootCondition implements ConfigurationCondition { + private static final String AUTHORIZATION_ANNOTATION = "org.springframework." + + "security.oauth2.config.annotation.web.configuration." + + "AuthorizationServerEndpointsConfiguration"; + @Override public ConfigurationPhase getConfigurationPhase() { return ConfigurationPhase.REGISTER_BEAN; @@ -104,31 +112,48 @@ public class OAuth2ResourceServerConfiguration { public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) { Environment environment = context.getEnvironment(); - RelaxedPropertyResolver resolver = new RelaxedPropertyResolver(environment); + RelaxedPropertyResolver resolver = new RelaxedPropertyResolver(environment, + "spring.oauth2.resource."); String client = environment .resolvePlaceholders("${spring.oauth2.client.clientId:}"); if (StringUtils.hasText(client)) { return ConditionOutcome.match("found client id"); } - if (!resolver.getSubProperties("spring.oauth2.resource.jwt").isEmpty()) { + if (!resolver.getSubProperties("jwt").isEmpty()) { return ConditionOutcome.match("found JWT resource configuration"); } - if (StringUtils.hasText(resolver - .getProperty("spring.oauth2.resource.userInfoUri"))) { - return ConditionOutcome - .match("found UserInfo URI resource configuration"); + if (StringUtils.hasText(resolver.getProperty("user-info-uri"))) { + return ConditionOutcome.match("found UserInfo " + + "URI resource configuration"); } - if (ClassUtils - .isPresent( - "org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerEndpointsConfiguration", - null)) { - if (SpringBootCondition.evaluateForClass(ResourceServerCondition.class, context)) { - return ConditionOutcome - .match("found authorization server endpoints configuration"); + if (ClassUtils.isPresent(AUTHORIZATION_ANNOTATION, null)) { + if (AuthorizationServerEndpointsConfigurationBeanCondition + .matches(context)) { + return ConditionOutcome.match("found authorization " + + "server endpoints configuration"); } } - return ConditionOutcome - .noMatch("found neither client id nor JWT resource nor authorization server"); + return ConditionOutcome.noMatch("found neither client id nor " + + "JWT resource nor authorization server"); + } + + } + + @ConditionalOnBean(AuthorizationServerEndpointsConfiguration.class) + private static class AuthorizationServerEndpointsConfigurationBeanCondition { + + public static boolean matches(ConditionContext context) { + Class type = AuthorizationServerEndpointsConfigurationBeanCondition.class; + Conditional conditional = AnnotationUtils.findAnnotation(type, + Conditional.class); + StandardAnnotationMetadata metadata = new StandardAnnotationMetadata(type); + for (Class conditionType : conditional.value()) { + Condition condition = BeanUtils.instantiateClass(conditionType); + if (condition.matches(context, metadata)) { + return true; + } + } + return false; } } diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/ResourceServerProperties.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/ResourceServerProperties.java index 5e26e7ae7a..b3cc12da0b 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/ResourceServerProperties.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/ResourceServerProperties.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2014 the original author or authors. + * Copyright 2012-2015 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. @@ -30,8 +30,10 @@ import org.springframework.validation.Validator; import com.fasterxml.jackson.annotation.JsonIgnore; /** - * @author Dave Syer + * Configuration properties for OAuth2 Resources. * + * @author Dave Syer + * @since 1.3.0 */ @ConfigurationProperties("spring.oauth2.resource") public class ResourceServerProperties implements Validator, BeanFactoryAware { @@ -71,7 +73,7 @@ public class ResourceServerProperties implements Validator, BeanFactoryAware { * The token type to send when using the userInfoUri. */ private String tokenType = DefaultOAuth2AccessToken.BEARER_TYPE; - + private Jwt jwt = new Jwt(); public ResourceServerProperties() { @@ -133,7 +135,7 @@ public class ResourceServerProperties implements Validator, BeanFactoryAware { } public String getTokenType() { - return tokenType; + return this.tokenType; } public void setTokenType(String tokenType) { @@ -187,7 +189,8 @@ public class ResourceServerProperties implements Validator, BeanFactoryAware { } if (!StringUtils.hasText(resource.getUserInfoUri())) { errors.rejectValue("tokenInfoUri", "missing.tokenInfoUri", - "Missing tokenInfoUri and userInfoUri and there is no JWT verifier key"); + "Missing tokenInfoUri and userInfoUri and there is no " + + "JWT verifier key"); } } } 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 14c99217b5..562e80277b 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 @@ -1,5 +1,5 @@ /* - * Copyright 2013-2014 the original author or authors. + * Copyright 2012-2015 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. @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.boot.autoconfigure.security.oauth2.resource; import java.io.IOException; @@ -31,11 +32,11 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass; import org.springframework.boot.autoconfigure.condition.SpringBootCondition; +import org.springframework.boot.bind.RelaxedPropertyResolver; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ConditionContext; import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; -import org.springframework.core.OrderComparator; import org.springframework.core.annotation.AnnotationAwareOrderComparator; import org.springframework.core.env.Environment; import org.springframework.core.type.AnnotatedTypeMetadata; @@ -71,8 +72,10 @@ import org.springframework.web.client.ResourceAccessException; import org.springframework.web.client.RestTemplate; /** - * @author Dave Syer + * Configuration for an OAuth2 resource server. * + * @author Dave Syer + * @since 1.3.0 */ @Configuration @ConditionalOnMissingBean(AuthorizationServerEndpointsConfiguration.class) @@ -88,10 +91,10 @@ public class ResourceServerTokenServicesConfiguration { static { DEFAULT_RESOURCE_DETAILS.setClientId(""); - DEFAULT_RESOURCE_DETAILS - .setUserAuthorizationUri("Not a URI because there is no client"); - DEFAULT_RESOURCE_DETAILS - .setAccessTokenUri("Not a URI because there is no client"); + DEFAULT_RESOURCE_DETAILS.setUserAuthorizationUri("Not a URI " + + "because there is no client"); + DEFAULT_RESOURCE_DETAILS.setAccessTokenUri("Not a URI " + + "because there is no client"); } @Autowired(required = false) @@ -106,52 +109,37 @@ public class ResourceServerTokenServicesConfiguration { @Bean(name = "userInfoRestTemplate") public OAuth2RestTemplate userInfoRestTemplate() { - OAuth2RestTemplate template; - if (details == null) { - details = DEFAULT_RESOURCE_DETAILS; - } - if (oauth2ClientContext == null) { - template = new OAuth2RestTemplate(details); - } - else { - template = new OAuth2RestTemplate(details, oauth2ClientContext); + if (this.details == null) { + this.details = DEFAULT_RESOURCE_DETAILS; } + OAuth2RestTemplate template = getTemplate(); template.setInterceptors(Arrays - . asList(new ClientHttpRequestInterceptor() { - @Override - public ClientHttpResponse intercept(HttpRequest request, - byte[] body, ClientHttpRequestExecution execution) - throws IOException { - request.getHeaders().setAccept( - Arrays.asList(MediaType.APPLICATION_JSON)); - return execution.execute(request, body); - } - })); + . asList(new AcceptJsonRequestInterceptor())); AuthorizationCodeAccessTokenProvider accessTokenProvider = new AuthorizationCodeAccessTokenProvider(); - accessTokenProvider.setTokenRequestEnhancer(new RequestEnhancer() { - @Override - public void enhance(AccessTokenRequest request, - OAuth2ProtectedResourceDetails resource, - MultiValueMap form, HttpHeaders headers) { - headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON)); - } - }); + accessTokenProvider.setTokenRequestEnhancer(new AcceptJsonRequestEnhancer()); template.setAccessTokenProvider(accessTokenProvider); - OrderComparator.sort(customizers); - for (UserInfoRestTemplateCustomizer customizer : customizers) { + AnnotationAwareOrderComparator.sort(this.customizers); + for (UserInfoRestTemplateCustomizer customizer : this.customizers) { customizer.customize(template); } return template; } + private OAuth2RestTemplate getTemplate() { + if (this.oauth2ClientContext == null) { + return new OAuth2RestTemplate(this.details); + } + return new OAuth2RestTemplate(this.details, this.oauth2ClientContext); + } + } @Configuration - @Conditional(NotJwtToken.class) + @Conditional(NotJwtTokenCondition.class) protected static class RemoteTokenServicesConfiguration { @Configuration - @Conditional(TokenInfo.class) + @Conditional(TokenInfoCondition.class) protected static class TokenInfoServicesConfiguration { @Autowired @@ -170,7 +158,7 @@ public class ResourceServerTokenServicesConfiguration { @Configuration @ConditionalOnClass(OAuth2ConnectionFactory.class) - @Conditional(NotTokenInfo.class) + @Conditional(NotTokenInfoCondition.class) protected static class SocialTokenServicesConfiguration { @Autowired @@ -197,8 +185,8 @@ public class ResourceServerTokenServicesConfiguration { public ResourceServerTokenServices userInfoTokenServices() { UserInfoTokenServices services = new UserInfoTokenServices( this.sso.getUserInfoUri(), this.sso.getClientId()); - services.setTokenType(sso.getTokenType()); - services.setRestTemplate(restTemplate); + services.setTokenType(this.sso.getTokenType()); + services.setRestTemplate(this.restTemplate); return services; } @@ -206,7 +194,7 @@ public class ResourceServerTokenServicesConfiguration { @Configuration @ConditionalOnMissingClass("org.springframework.social.connect.support.OAuth2ConnectionFactory") - @Conditional(NotTokenInfo.class) + @Conditional(NotTokenInfoCondition.class) protected static class UserInfoTokenServicesConfiguration { @Autowired @@ -221,8 +209,8 @@ public class ResourceServerTokenServicesConfiguration { public ResourceServerTokenServices userInfoTokenServices() { UserInfoTokenServices services = new UserInfoTokenServices( this.sso.getUserInfoUri(), this.sso.getClientId()); - services.setRestTemplate(restTemplate); - services.setTokenType(sso.getTokenType()); + services.setRestTemplate(this.restTemplate); + services.setTokenType(this.sso.getTokenType()); return services; } @@ -231,7 +219,7 @@ public class ResourceServerTokenServicesConfiguration { } @Configuration - @Conditional(JwtToken.class) + @Conditional(JwtTokenCondition.class) protected static class JwtTokenServicesConfiguration { private RestTemplate keyUriRestTemplate = new RestTemplate(); @@ -262,22 +250,11 @@ public class ResourceServerTokenServicesConfiguration { String keyValue = this.resource.getJwt().getKeyValue(); if (!StringUtils.hasText(keyValue)) { try { - HttpHeaders headers = new HttpHeaders(); - if (resource.getClientId() != null - && resource.getClientSecret() != null) { - byte[] token = Base64 - .encode((resource.getClientId() + ":" + resource - .getClientSecret()).getBytes()); - headers.add("Authorization", "Basic " + new String(token)); - } - HttpEntity requestEntity = new HttpEntity(headers); - keyValue = (String) keyUriRestTemplate - .exchange(resource.getJwt().getKeyUri(), HttpMethod.GET, - requestEntity, Map.class).getBody().get("value"); + keyValue = getKeyFromServer(); } - catch (ResourceAccessException e) { - // ignore - logger.warn("Failed to fetch token key (you may need to refresh when the auth server is back)"); + catch (ResourceAccessException ex) { + logger.warn("Failed to fetch token key (you may need to refresh " + + "when the auth server is back)"); } } if (StringUtils.hasText(keyValue) && !keyValue.startsWith("-----BEGIN")) { @@ -286,52 +263,68 @@ public class ResourceServerTokenServicesConfiguration { if (keyValue != null) { converter.setVerifierKey(keyValue); } - AnnotationAwareOrderComparator.sort(configurers); - for (JwtAccessTokenConverterConfigurer configurer : configurers) { + AnnotationAwareOrderComparator.sort(this.configurers); + for (JwtAccessTokenConverterConfigurer configurer : this.configurers) { configurer.configure(converter); } return converter; } + private String getKeyFromServer() { + HttpHeaders headers = new HttpHeaders(); + String username = this.resource.getClientId(); + String password = this.resource.getClientSecret(); + if (username != null && password != null) { + byte[] token = Base64.encode((username + ":" + password).getBytes()); + headers.add("Authorization", "Basic " + new String(token)); + } + HttpEntity request = new HttpEntity(headers); + String url = this.resource.getJwt().getKeyUri(); + return (String) this.keyUriRestTemplate + .exchange(url, HttpMethod.GET, request, Map.class).getBody() + .get("value"); + } + } - private static class TokenInfo extends SpringBootCondition { + private static class TokenInfoCondition extends SpringBootCondition { @Override public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) { Environment environment = context.getEnvironment(); - boolean preferTokenInfo = environment - .resolvePlaceholders( - "${spring.oauth2.resource.preferTokenInfo:${OAUTH2_RESOURCE_PREFERTOKENINFO:true}}") - .equals("true"); - boolean hasTokenInfo = !environment.resolvePlaceholders( - "${spring.oauth2.resource.tokenInfoUri:}").equals(""); - boolean hasUserInfo = !environment.resolvePlaceholders( - "${spring.oauth2.resource.userInfoUri:}").equals(""); - if (!hasUserInfo) { + RelaxedPropertyResolver resolver = new RelaxedPropertyResolver(environment, + "spring.oauth2.resource."); + Boolean preferTokenInfo = resolver.getProperty("prefer-token-info", + Boolean.class); + if (preferTokenInfo == null) { + preferTokenInfo = environment.resolvePlaceholders( + "${OAUTH2_RESOURCE_PREFERTOKENINFO:true}").equals("true"); + } + String tokenInfoUri = resolver.getProperty("token-info-uri"); + String userInfoUri = resolver.getProperty("user-info-uri"); + if (!StringUtils.hasLength(userInfoUri)) { return ConditionOutcome.match("No user info provided"); } - if (hasTokenInfo) { - if (preferTokenInfo) { - return ConditionOutcome - .match("Token info endpoint is preferred and user info provided"); - } + if (StringUtils.hasLength(tokenInfoUri) && preferTokenInfo) { + return ConditionOutcome.match("Token info endpoint " + + "is preferred and user info provided"); } return ConditionOutcome.noMatch("Token info endpoint is not provided"); } } - private static class JwtToken extends SpringBootCondition { + private static class JwtTokenCondition extends SpringBootCondition { @Override public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) { - if (StringUtils.hasText(context.getEnvironment().getProperty( - "spring.oauth2.resource.jwt.keyValue")) - || StringUtils.hasText(context.getEnvironment().getProperty( - "spring.oauth2.resource.jwt.keyUri"))) { + RelaxedPropertyResolver resolver = new RelaxedPropertyResolver( + context.getEnvironment(), "spring.oauth2.resource.jwt."); + String keyValue = resolver.getProperty("key-value"); + String keyUri = resolver.getProperty("key-uri"); + if (StringUtils.hasText(keyValue) || StringUtils.hasText(keyUri)) { return ConditionOutcome.match("public key is provided"); } return ConditionOutcome.noMatch("public key is not provided"); @@ -339,35 +332,53 @@ public class ResourceServerTokenServicesConfiguration { } - private static class NotTokenInfo extends SpringBootCondition { + private static class NotTokenInfoCondition extends SpringBootCondition { - private TokenInfo opposite = new TokenInfo(); + private TokenInfoCondition tokenInfoCondition = new TokenInfoCondition(); @Override public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) { - ConditionOutcome outcome = this.opposite.getMatchOutcome(context, metadata); - if (outcome.isMatch()) { - return ConditionOutcome.noMatch(outcome.getMessage()); - } - return ConditionOutcome.match(outcome.getMessage()); + return ConditionOutcome.inverse(this.tokenInfoCondition.getMatchOutcome( + context, metadata)); } } - private static class NotJwtToken extends SpringBootCondition { + private static class NotJwtTokenCondition extends SpringBootCondition { - private JwtToken opposite = new JwtToken(); + private JwtTokenCondition jwtTokenCondition = new JwtTokenCondition(); @Override public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) { - ConditionOutcome outcome = this.opposite.getMatchOutcome(context, metadata); - if (outcome.isMatch()) { - return ConditionOutcome.noMatch(outcome.getMessage()); - } - return ConditionOutcome.match(outcome.getMessage()); + return ConditionOutcome.inverse(this.jwtTokenCondition.getMatchOutcome( + context, metadata)); } } + + private static class AcceptJsonRequestInterceptor implements + ClientHttpRequestInterceptor { + + @Override + public ClientHttpResponse intercept(HttpRequest request, byte[] body, + ClientHttpRequestExecution execution) throws IOException { + request.getHeaders().setAccept(Arrays.asList(MediaType.APPLICATION_JSON)); + return execution.execute(request, body); + } + + } + + private static class AcceptJsonRequestEnhancer implements RequestEnhancer { + + @Override + public void enhance(AccessTokenRequest request, + OAuth2ProtectedResourceDetails resource, + MultiValueMap form, HttpHeaders headers) { + headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON)); + } + + } + } diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/SpringSocialTokenServices.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/SpringSocialTokenServices.java index 170f73c38e..87b3ff2a28 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/SpringSocialTokenServices.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/SpringSocialTokenServices.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2014 the original author or authors. + * Copyright 2012-2015 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. @@ -13,12 +13,16 @@ * 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.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.AuthenticationException; +import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.AuthorityUtils; import org.springframework.security.oauth2.common.OAuth2AccessToken; import org.springframework.security.oauth2.common.exceptions.InvalidTokenException; @@ -31,16 +35,18 @@ import org.springframework.social.connect.support.OAuth2ConnectionFactory; import org.springframework.social.oauth2.AccessGrant; /** - * @author Dave Syer + * {@link ResourceServerTokenServices} backed by Spring Social. * + * @author Dave Syer + * @since 1.3.0 */ public class SpringSocialTokenServices implements ResourceServerTokenServices { protected final Log logger = LogFactory.getLog(getClass()); - private OAuth2ConnectionFactory connectionFactory; + private final OAuth2ConnectionFactory connectionFactory; - private String clientId; + private final String clientId; public SpringSocialTokenServices(OAuth2ConnectionFactory connectionFactory, String clientId) { @@ -51,21 +57,20 @@ public class SpringSocialTokenServices implements ResourceServerTokenServices { @Override public OAuth2Authentication loadAuthentication(String accessToken) throws AuthenticationException, InvalidTokenException { - - Connection connection = connectionFactory.createConnection(new AccessGrant( - accessToken)); + AccessGrant accessGrant = new AccessGrant(accessToken); + Connection connection = this.connectionFactory.createConnection(accessGrant); UserProfile user = connection.fetchUserProfile(); return extractAuthentication(user); } private OAuth2Authentication extractAuthentication(UserProfile user) { - UsernamePasswordAuthenticationToken principal = new UsernamePasswordAuthenticationToken( - user.getUsername(), "N/A", - AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_USER")); - principal.setDetails(user); - OAuth2Request request = new OAuth2Request(null, clientId, null, true, null, null, - null, null, null); - return new OAuth2Authentication(request, principal); + String principal = user.getUsername(); + List authorities = AuthorityUtils + .commaSeparatedStringToAuthorityList("ROLE_USER"); + OAuth2Request request = new OAuth2Request(null, this.clientId, null, true, null, + null, null, null, null); + return new OAuth2Authentication(request, new UsernamePasswordAuthenticationToken( + principal, "N/A", authorities)); } @Override @@ -73,4 +78,4 @@ public class SpringSocialTokenServices implements ResourceServerTokenServices { throw new UnsupportedOperationException("Not supported: read access token"); } -} \ No newline at end of file +} diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/UserInfoRestTemplateCustomizer.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/UserInfoRestTemplateCustomizer.java index fdc1f11ec0..ad9074743b 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/UserInfoRestTemplateCustomizer.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/UserInfoRestTemplateCustomizer.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2015 the original author or authors. + * Copyright 2012-2015 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. @@ -25,15 +25,15 @@ import org.springframework.security.oauth2.client.OAuth2RestTemplate; * authenticator (which is how the token gets attached to outgoing requests). The rest * template that is being customized here is only used internally to carry out * authentication (in the SSO or Resource Server use cases). - * - * @author Dave Syer * + * @author Dave Syer + * @since 1.3.0 */ public interface UserInfoRestTemplateCustomizer { /** * Customize the rest template before it is initialized. - * + * * @param template the rest template */ void customize(OAuth2RestTemplate template); diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/UserInfoTokenServices.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/UserInfoTokenServices.java index 6909998bf9..fb7a584a78 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/UserInfoTokenServices.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/UserInfoTokenServices.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2014 the original author or authors. + * Copyright 2012-2015 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. @@ -13,14 +13,17 @@ * 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 java.util.Map; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.AuthenticationException; +import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.AuthorityUtils; import org.springframework.security.oauth2.client.OAuth2RestOperations; import org.springframework.security.oauth2.client.OAuth2RestTemplate; @@ -32,13 +35,22 @@ import org.springframework.security.oauth2.provider.OAuth2Authentication; import org.springframework.security.oauth2.provider.OAuth2Request; import org.springframework.security.oauth2.provider.token.ResourceServerTokenServices; +/** + * {@link ResourceServerTokenServices} that uses a user info REST service. + * + * @author Dave Syer + * @since 1.3.0 + */ public class UserInfoTokenServices implements ResourceServerTokenServices { protected final Log logger = LogFactory.getLog(getClass()); - private String userInfoEndpointUrl; + private static final String[] PRINCIPAL_KEYS = new String[] { "user", "username", + "userid", "user_id", "login", "id", "name" }; - private String clientId; + private final String userInfoEndpointUrl; + + private final String clientId; private OAuth2RestOperations restTemplate; @@ -48,7 +60,7 @@ public class UserInfoTokenServices implements ResourceServerTokenServices { this.userInfoEndpointUrl = userInfoEndpointUrl; this.clientId = clientId; } - + public void setTokenType(String tokenType) { this.tokenType = tokenType; } @@ -60,31 +72,28 @@ public class UserInfoTokenServices implements ResourceServerTokenServices { @Override public OAuth2Authentication loadAuthentication(String accessToken) throws AuthenticationException, InvalidTokenException { - - Map map = getMap(userInfoEndpointUrl, accessToken); - + Map map = getMap(this.userInfoEndpointUrl, accessToken); if (map.containsKey("error")) { - logger.debug("userinfo returned error: " + map.get("error")); + this.logger.debug("userinfo returned error: " + map.get("error")); throw new InvalidTokenException(accessToken); } - return extractAuthentication(map); } private OAuth2Authentication extractAuthentication(Map map) { - UsernamePasswordAuthenticationToken user = new UsernamePasswordAuthenticationToken( - getPrincipal(map), "N/A", - AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_USER")); - user.setDetails(map); - OAuth2Request request = new OAuth2Request(null, clientId, null, true, null, null, - null, null, null); - return new OAuth2Authentication(request, user); + Object principal = getPrincipal(map); + List authorities = AuthorityUtils + .commaSeparatedStringToAuthorityList("ROLE_USER"); + OAuth2Request request = new OAuth2Request(null, this.clientId, null, true, null, + null, null, null, null); + UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken( + principal, "N/A", authorities); + token.setDetails(map); + return new OAuth2Authentication(request, token); } private Object getPrincipal(Map map) { - String[] keys = new String[] { "user", "username", "userid", "user_id", "login", - "id", "name" }; - for (String key : keys) { + for (String key : PRINCIPAL_KEYS) { if (map.containsKey(key)) { return map.get(key); } @@ -97,22 +106,19 @@ public class UserInfoTokenServices implements ResourceServerTokenServices { throw new UnsupportedOperationException("Not supported: read access token"); } + @SuppressWarnings({ "unchecked" }) private Map getMap(String path, String accessToken) { - logger.info("Getting user info from: " + path); + this.logger.info("Getting user info from: " + path); OAuth2RestOperations restTemplate = this.restTemplate; if (restTemplate == null) { BaseOAuth2ProtectedResourceDetails resource = new BaseOAuth2ProtectedResourceDetails(); - resource.setClientId(clientId); + resource.setClientId(this.clientId); restTemplate = new OAuth2RestTemplate(resource); } DefaultOAuth2AccessToken token = new DefaultOAuth2AccessToken(accessToken); - token.setTokenType(tokenType); + token.setTokenType(this.tokenType); restTemplate.getOAuth2ClientContext().setAccessToken(token); - @SuppressWarnings("rawtypes") - Map map = restTemplate.getForEntity(path, Map.class).getBody(); - @SuppressWarnings("unchecked") - Map result = map; - return result; + return restTemplate.getForEntity(path, Map.class).getBody(); } -} \ No newline at end of file +} diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/SpringSecurityOAuth2AutoConfigurationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/SpringSecurityOAuth2AutoConfigurationTests.java index 6275800212..1dbfe67436 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/SpringSecurityOAuth2AutoConfigurationTests.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/SpringSecurityOAuth2AutoConfigurationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2014 the original author or authors. + * Copyright 2012-2015 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. @@ -16,10 +16,6 @@ package org.springframework.boot.autoconfigure.security.oauth2; -import static org.hamcrest.CoreMatchers.equalTo; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.MatcherAssert.assertThat; - import java.net.URI; import java.util.Arrays; import java.util.List; @@ -30,8 +26,8 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration; import org.springframework.boot.autoconfigure.security.oauth2.authserver.SpringSecurityOAuth2AuthorizationServerConfiguration; import org.springframework.boot.autoconfigure.security.oauth2.method.OAuth2MethodSecurityConfiguration; -import org.springframework.boot.autoconfigure.security.oauth2.resource.ResourceServerProperties; import org.springframework.boot.autoconfigure.security.oauth2.resource.OAuth2ResourceServerConfiguration; +import org.springframework.boot.autoconfigure.security.oauth2.resource.ResourceServerProperties; import org.springframework.boot.autoconfigure.web.DispatcherServletAutoConfiguration; import org.springframework.boot.autoconfigure.web.HttpMessageConvertersAutoConfiguration; import org.springframework.boot.autoconfigure.web.ServerPropertiesAutoConfiguration; @@ -93,6 +89,9 @@ import org.springframework.web.client.RestTemplate; import com.fasterxml.jackson.databind.JsonNode; +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.MatcherAssert.assertThat; + /** * Verify Spring Security OAuth2 auto-configuration secures end points properly, accepts * environmental overrides, and also backs off in the presence of other @@ -103,6 +102,10 @@ import com.fasterxml.jackson.databind.JsonNode; */ public class SpringSecurityOAuth2AutoConfigurationTests { + private static final Class RESOURCE_SERVER_CONFIG = OAuth2ResourceServerConfiguration.class; + + private static final Class AUTHORIZATION_SERVER_CONFIG = SpringSecurityOAuth2AuthorizationServerConfiguration.class; + private AnnotationConfigEmbeddedWebApplicationContext context; @Test @@ -111,11 +114,9 @@ public class SpringSecurityOAuth2AutoConfigurationTests { this.context.register(AuthorizationAndResourceServerConfiguration.class, MinimalSecureWebApplication.class); this.context.refresh(); - - this.context.getBean(SpringSecurityOAuth2AuthorizationServerConfiguration.class); - this.context.getBean(OAuth2ResourceServerConfiguration.class); + this.context.getBean(AUTHORIZATION_SERVER_CONFIG); + this.context.getBean(RESOURCE_SERVER_CONFIG); this.context.getBean(OAuth2MethodSecurityConfiguration.class); - ClientDetails config = this.context.getBean(BaseClientDetails.class); AuthorizationEndpoint endpoint = this.context .getBean(AuthorizationEndpoint.class); @@ -125,15 +126,11 @@ public class SpringSecurityOAuth2AutoConfigurationTests { .getBean(ClientDetailsService.class); ClientDetails clientDetails = clientDetailsService.loadClientByClientId(config .getClientId()); - - assertThat(AopUtils.isJdkDynamicProxy(clientDetailsService), is(true)); + assertThat(AopUtils.isJdkDynamicProxy(clientDetailsService), equalTo(true)); assertThat(AopUtils.getTargetClass(clientDetailsService).getName(), - is(ClientDetailsService.class.getName())); - - assertThat(handler instanceof ApprovalStoreUserApprovalHandler, is(true)); - + equalTo(ClientDetailsService.class.getName())); + assertThat(handler instanceof ApprovalStoreUserApprovalHandler, equalTo(true)); assertThat(clientDetails, equalTo(config)); - verifyAuthentication(config); } @@ -146,12 +143,9 @@ public class SpringSecurityOAuth2AutoConfigurationTests { this.context.register(AuthorizationAndResourceServerConfiguration.class, MinimalSecureWebApplication.class); this.context.refresh(); - ClientDetails config = this.context.getBean(ClientDetails.class); - - assertThat(config.getClientId(), is("myclientid")); - assertThat(config.getClientSecret(), is("mysecret")); - + assertThat(config.getClientId(), equalTo("myclientid")); + assertThat(config.getClientSecret(), equalTo("mysecret")); verifyAuthentication(config); } @@ -161,16 +155,8 @@ public class SpringSecurityOAuth2AutoConfigurationTests { this.context.register(AuthorizationServerConfiguration.class, MinimalSecureWebApplication.class); this.context.refresh(); - - assertThat( - this.context - .getBeanNamesForType(OAuth2ResourceServerConfiguration.class).length, - is(0)); - - assertThat( - this.context - .getBeanNamesForType(SpringSecurityOAuth2AuthorizationServerConfiguration.class).length, - is(1)); + assertThat(countBeans(RESOURCE_SERVER_CONFIG), equalTo(0)); + assertThat(countBeans(AUTHORIZATION_SERVER_CONFIG), equalTo(1)); } @Test @@ -181,21 +167,10 @@ public class SpringSecurityOAuth2AutoConfigurationTests { EnvironmentTestUtils.addEnvironment(this.context, "spring.oauth2.resource.jwt.keyValue:DEADBEEF"); this.context.refresh(); - - assertThat( - this.context - .getBeanNamesForType(OAuth2ResourceServerConfiguration.class).length, - is(1)); - - assertThat( - this.context - .getBeanNamesForType(SpringSecurityOAuth2AuthorizationServerConfiguration.class).length, - is(0)); - - assertThat(this.context.getBeanNamesForType(UserApprovalHandler.class).length, - is(0)); - assertThat(this.context.getBeanNamesForType(DefaultTokenServices.class).length, - is(1)); + assertThat(countBeans(RESOURCE_SERVER_CONFIG), equalTo(1)); + assertThat(countBeans(AUTHORIZATION_SERVER_CONFIG), equalTo(0)); + assertThat(countBeans(UserApprovalHandler.class), equalTo(0)); + assertThat(countBeans(DefaultTokenServices.class), equalTo(1)); } @Test @@ -204,24 +179,11 @@ public class SpringSecurityOAuth2AutoConfigurationTests { this.context.register(AuthorizationAndResourceServerConfiguration.class, CustomResourceServer.class, MinimalSecureWebApplication.class); this.context.refresh(); - ClientDetails config = this.context.getBean(ClientDetails.class); - - assertThat( - this.context - .getBeanNamesForType(SpringSecurityOAuth2AuthorizationServerConfiguration.class).length, - is(1)); - - assertThat(this.context.getBeanNamesForType(CustomResourceServer.class).length, - is(1)); - - assertThat( - this.context - .getBeanNamesForType(OAuth2ResourceServerConfiguration.class).length, - is(1)); - + assertThat(countBeans(AUTHORIZATION_SERVER_CONFIG), equalTo(1)); + assertThat(countBeans(CustomResourceServer.class), equalTo(1)); + assertThat(countBeans(RESOURCE_SERVER_CONFIG), equalTo(1)); verifyAuthentication(config); - } @Test @@ -232,7 +194,6 @@ public class SpringSecurityOAuth2AutoConfigurationTests { this.context.register(AuthorizationAndResourceServerConfiguration.class, CustomAuthorizationServer.class, MinimalSecureWebApplication.class); this.context.refresh(); - BaseClientDetails config = new BaseClientDetails(); config.setClientId("client"); config.setClientSecret("secret"); @@ -240,17 +201,8 @@ public class SpringSecurityOAuth2AutoConfigurationTests { config.setAuthorizedGrantTypes(Arrays.asList("password")); config.setAuthorities(AuthorityUtils.commaSeparatedStringToAuthorityList("USER")); config.setScope(Arrays.asList("read")); - - assertThat( - this.context - .getBeanNamesForType(SpringSecurityOAuth2AuthorizationServerConfiguration.class).length, - is(0)); - - assertThat( - this.context - .getBeanNamesForType(OAuth2ResourceServerConfiguration.class).length, - is(1)); - + assertThat(countBeans(AUTHORIZATION_SERVER_CONFIG), equalTo(0)); + assertThat(countBeans(RESOURCE_SERVER_CONFIG), equalTo(1)); verifyAuthentication(config); } @@ -260,20 +212,15 @@ public class SpringSecurityOAuth2AutoConfigurationTests { this.context.register(AuthorizationAndResourceServerConfiguration.class, MinimalSecureWebApplication.class); this.context.refresh(); - this.context.getBean(OAuth2MethodSecurityConfiguration.class); - ClientDetails config = this.context.getBean(ClientDetails.class); - DelegatingMethodSecurityMetadataSource source = this.context .getBean(DelegatingMethodSecurityMetadataSource.class); List sources = source .getMethodSecurityMetadataSources(); - - assertThat(sources.size(), is(1)); + assertThat(sources.size(), equalTo(1)); assertThat(sources.get(0).getClass().getName(), - is(PrePostAnnotationSecurityMetadataSource.class.getName())); - + equalTo(PrePostAnnotationSecurityMetadataSource.class.getName())); verifyAuthentication(config); } @@ -283,20 +230,15 @@ public class SpringSecurityOAuth2AutoConfigurationTests { this.context.register(SecuredEnabledConfiguration.class, MinimalSecureWebApplication.class); this.context.refresh(); - this.context.getBean(OAuth2MethodSecurityConfiguration.class); - ClientDetails config = this.context.getBean(ClientDetails.class); - DelegatingMethodSecurityMetadataSource source = this.context .getBean(DelegatingMethodSecurityMetadataSource.class); List sources = source .getMethodSecurityMetadataSources(); - - assertThat(sources.size(), is(1)); + assertThat(sources.size(), equalTo(1)); assertThat(sources.get(0).getClass().getName(), - is(SecuredAnnotationSecurityMetadataSource.class.getName())); - + equalTo(SecuredAnnotationSecurityMetadataSource.class.getName())); verifyAuthentication(config, HttpStatus.OK); } @@ -306,20 +248,15 @@ public class SpringSecurityOAuth2AutoConfigurationTests { this.context.register(Jsr250EnabledConfiguration.class, MinimalSecureWebApplication.class); this.context.refresh(); - this.context.getBean(OAuth2MethodSecurityConfiguration.class); - ClientDetails config = this.context.getBean(ClientDetails.class); - DelegatingMethodSecurityMetadataSource source = this.context .getBean(DelegatingMethodSecurityMetadataSource.class); List sources = source .getMethodSecurityMetadataSources(); - - assertThat(sources.size(), is(1)); + assertThat(sources.size(), equalTo(1)); assertThat(sources.get(0).getClass().getName(), - is(Jsr250MethodSecurityMetadataSource.class.getName())); - + equalTo(Jsr250MethodSecurityMetadataSource.class.getName())); verifyAuthentication(config, HttpStatus.OK); } @@ -329,20 +266,18 @@ public class SpringSecurityOAuth2AutoConfigurationTests { this.context.register(CustomMethodSecurity.class, TestSecurityConfiguration.class, MinimalSecureWebApplication.class); this.context.refresh(); - DelegatingMethodSecurityMetadataSource source = this.context .getBean(DelegatingMethodSecurityMetadataSource.class); List sources = source .getMethodSecurityMetadataSources(); - assertThat(sources.size(), is(1)); + assertThat(sources.size(), equalTo(1)); assertThat(sources.get(0).getClass().getName(), - is(PrePostAnnotationSecurityMetadataSource.class.getName())); + equalTo(PrePostAnnotationSecurityMetadataSource.class.getName())); } /** * Connect to the oauth service, get a token, and then attempt some operations using * it. - * * @param config */ private void verifyAuthentication(ClientDetails config) { @@ -352,60 +287,66 @@ public class SpringSecurityOAuth2AutoConfigurationTests { private void verifyAuthentication(ClientDetails config, HttpStatus finalStatus) { String baseUrl = "http://localhost:" + this.context.getEmbeddedServletContainer().getPort(); - RestTemplate rest = new TestRestTemplate(); - HttpHeaders headers = new HttpHeaders(); - // First, verify the web endpoint can't be reached - ResponseEntity entity = rest.exchange(new RequestEntity(headers, - HttpMethod.GET, URI.create(baseUrl + "/secured")), String.class); - assertThat(entity.getStatusCode(), is(HttpStatus.UNAUTHORIZED)); - + assertEndpointUnauthorized(baseUrl, rest); // Since we can't reach it, need to collect an authorization token - String base64Creds = new String( - Base64.encode((config.getClientId() + ":" + config.getClientSecret()) - .getBytes())); - headers.set("Authorization", "Basic " + base64Creds); + HttpHeaders headers = getHeaders(config); + String url = baseUrl + "/oauth/token"; + JsonNode tokenResponse = rest.postForObject(url, + new HttpEntity>(getBody(), headers), + JsonNode.class); + String authorizationToken = tokenResponse.findValue("access_token").asText(); + String tokenType = tokenResponse.findValue("token_type").asText(); + String scope = tokenResponse.findValues("scope").get(0).toString(); + assertThat(tokenType, equalTo("bearer")); + assertThat(scope, equalTo("\"read\"")); + // Now we should be able to see that endpoint. + headers.set("Authorization", "BEARER " + authorizationToken); + ResponseEntity securedResponse = rest.exchange(new RequestEntity( + headers, HttpMethod.GET, URI.create(baseUrl + "/securedFind")), + String.class); + assertThat(securedResponse.getStatusCode(), equalTo(HttpStatus.OK)); + assertThat(securedResponse.getBody(), equalTo("You reached an endpoint " + + "secured by Spring Security OAuth2")); + ResponseEntity entity = rest.exchange(new RequestEntity(headers, + HttpMethod.POST, URI.create(baseUrl + "/securedSave")), String.class); + assertThat(entity.getStatusCode(), equalTo(finalStatus)); + } + private HttpHeaders getHeaders(ClientDetails config) { + HttpHeaders headers = new HttpHeaders(); + String token = new String(Base64.encode((config.getClientId() + ":" + config + .getClientSecret()).getBytes())); + headers.set("Authorization", "Basic " + token); + return headers; + } + + private MultiValueMap getBody() { MultiValueMap body = new LinkedMultiValueMap(); body.set("grant_type", "password"); body.set("username", "foo"); body.set("password", "bar"); body.set("scope", "read"); + return body; + } - HttpEntity> request = new HttpEntity>( - body, headers); + private void assertEndpointUnauthorized(String baseUrl, RestTemplate rest) { + URI uri = URI.create(baseUrl + "/secured"); + ResponseEntity entity = rest.exchange(new RequestEntity( + HttpMethod.GET, uri), String.class); + assertThat(entity.getStatusCode(), equalTo(HttpStatus.UNAUTHORIZED)); + } - JsonNode response = rest.postForObject(baseUrl + "/oauth/token", request, - JsonNode.class); - String authorizationToken = response.findValue("access_token").asText(); - String tokenType = response.findValue("token_type").asText(); - String scope = response.findValues("scope").get(0).toString(); - assertThat(tokenType, is("bearer")); - assertThat(scope, is("\"read\"")); - - // Now we should be able to see that endpoint. - headers.set("Authorization", "BEARER " + authorizationToken); - - ResponseEntity securedResponse = rest.exchange(new RequestEntity( - headers, HttpMethod.GET, URI.create(baseUrl + "/securedFind")), - String.class); - assertThat(securedResponse.getStatusCode(), is(HttpStatus.OK)); - assertThat(securedResponse.getBody(), - is("You reached an endpoint secured by Spring Security OAuth2")); - - entity = rest.exchange( - new RequestEntity(headers, HttpMethod.POST, URI.create(baseUrl - + "/securedSave")), String.class); - assertThat(entity.getStatusCode(), is(finalStatus)); + private int countBeans(Class type) { + return this.context.getBeanNamesForType(type).length; } @Configuration @Import({ UseFreePortEmbeddedContainerConfiguration.class, SecurityAutoConfiguration.class, ServerPropertiesAutoConfiguration.class, - DispatcherServletAutoConfiguration.class, - OAuth2AutoConfiguration.class, WebMvcAutoConfiguration.class, - HttpMessageConvertersAutoConfiguration.class }) + DispatcherServletAutoConfiguration.class, OAuth2AutoConfiguration.class, + WebMvcAutoConfiguration.class, HttpMessageConvertersAutoConfiguration.class }) protected static class MinimalSecureWebApplication { } @@ -428,6 +369,7 @@ public class SpringSecurityOAuth2AutoConfigurationTests { TestWebApp testWebApp() { return new TestWebApp(); } + } @Configuration @@ -436,6 +378,7 @@ public class SpringSecurityOAuth2AutoConfigurationTests { @EnableGlobalMethodSecurity(prePostEnabled = true) protected static class AuthorizationAndResourceServerConfiguration extends TestSecurityConfiguration { + } @Configuration @@ -443,6 +386,7 @@ public class SpringSecurityOAuth2AutoConfigurationTests { @EnableResourceServer @EnableGlobalMethodSecurity(securedEnabled = true) protected static class SecuredEnabledConfiguration extends TestSecurityConfiguration { + } @Configuration @@ -450,17 +394,20 @@ public class SpringSecurityOAuth2AutoConfigurationTests { @EnableResourceServer @EnableGlobalMethodSecurity(jsr250Enabled = true) protected static class Jsr250EnabledConfiguration extends TestSecurityConfiguration { + } @Configuration @EnableAuthorizationServer protected static class AuthorizationServerConfiguration extends TestSecurityConfiguration { + } @Configuration @EnableResourceServer protected static class ResourceServerConfiguration extends TestSecurityConfiguration { + } @RestController @@ -477,14 +424,17 @@ public class SpringSecurityOAuth2AutoConfigurationTests { public String secureSave() { return "You reached an endpoint secured by Spring Security OAuth2"; } + } @Configuration protected static class UseFreePortEmbeddedContainerConfiguration { + @Bean TomcatEmbeddedServletContainerFactory containerFactory() { return new TomcatEmbeddedServletContainerFactory(0); } + } @Configuration @@ -544,14 +494,18 @@ public class SpringSecurityOAuth2AutoConfigurationTests { endpoints.tokenStore(tokenStore()).authenticationManager( this.authenticationManager); } + } @Configuration @EnableGlobalMethodSecurity(prePostEnabled = true) protected static class CustomMethodSecurity extends GlobalMethodSecurityConfiguration { + @Override protected MethodSecurityExpressionHandler createExpressionHandler() { return new OAuth2MethodSecurityExpressionHandler(); } + } + } diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/resource/ResourceServerPropertiesTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/resource/ResourceServerPropertiesTests.java index 4264c1fc6a..7a21a4c49b 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/resource/ResourceServerPropertiesTests.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/resource/ResourceServerPropertiesTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2014 the original author or authors. + * Copyright 2012-2015 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. @@ -13,9 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.boot.autoconfigure.security.oauth2.resource; -import static org.junit.Assert.assertNotNull; +package org.springframework.boot.autoconfigure.security.oauth2.resource; import java.util.Map; @@ -23,30 +22,34 @@ import org.junit.Test; import com.fasterxml.jackson.databind.ObjectMapper; +import static org.junit.Assert.assertNotNull; + /** - * @author Dave Syer + * Tests for {@link ResourceServerProperties}. * + * @author Dave Syer */ public class ResourceServerPropertiesTests { - private ResourceServerProperties properties = new ResourceServerProperties("client", "secret"); + private ResourceServerProperties properties = new ResourceServerProperties("client", + "secret"); @Test + @SuppressWarnings("unchecked") public void json() throws Exception { - properties.getJwt().setKeyUri("http://example.com/token_key"); + this.properties.getJwt().setKeyUri("http://example.com/token_key"); ObjectMapper mapper = new ObjectMapper(); - String json = mapper.writeValueAsString(properties); - @SuppressWarnings("unchecked") + String json = mapper.writeValueAsString(this.properties); Map value = mapper.readValue(json, Map.class); - @SuppressWarnings("unchecked") Map jwt = (Map) value.get("jwt"); assertNotNull("Wrong json: " + json, jwt.get("keyUri")); } @Test public void tokenKeyDerived() throws Exception { - properties.setUserInfoUri("http://example.com/userinfo"); - assertNotNull("Wrong properties: " + properties, properties.getJwt().getKeyUri()); + this.properties.setUserInfoUri("http://example.com/userinfo"); + assertNotNull("Wrong properties: " + this.properties, this.properties.getJwt() + .getKeyUri()); } } diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/resource/ResourceServerTokenServicesConfigurationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/resource/ResourceServerTokenServicesConfigurationTests.java index 328a67559c..9ea76eed83 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/resource/ResourceServerTokenServicesConfigurationTests.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/resource/ResourceServerTokenServicesConfigurationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2014 the original author or authors. + * Copyright 2012-2015 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. @@ -13,11 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.boot.autoconfigure.security.oauth2.resource; import org.junit.After; import org.junit.Test; -import org.mockito.Mockito; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration; import org.springframework.boot.autoconfigure.security.oauth2.OAuth2ClientProperties; @@ -38,13 +38,23 @@ import org.springframework.security.oauth2.provider.token.RemoteTokenServices; import org.springframework.social.connect.ConnectionFactoryLocator; import static org.junit.Assert.assertNotNull; +import static org.mockito.Mockito.mock; /** - * @author Dave Syer + * Tests for {@link ResourceServerTokenServicesConfiguration}. * + * @author Dave Syer */ public class ResourceServerTokenServicesConfigurationTests { + private static String PUBLIC_KEY = "-----BEGIN PUBLIC KEY-----\n" + + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnGp/Q5lh0P8nPL21oMMrt2RrkT9" + + "AW5jgYwLfSUnJVc9G6uR3cXRRDCjHqWU5WYwivcF180A6CWp/ireQFFBNowgc5XaA0kPpzE" + + "tgsA5YsNX7iSnUibB004iBTfU9hZ2Rbsc8cWqynT0RyN4TP1RYVSeVKvMQk4GT1r7JCEC+T" + + "Nu1ELmbNwMQyzKjsfBXyIOCFU/E94ktvsTZUHF4Oq44DBylCDsS1k7/sfZC2G5EU7Oz0mhG" + + "8+Uz6MSEQHtoIi6mc8u64Rwi3Z3tscuWG2ShtsUFuNSAFNkY7LkLn+/hxLCu2bNISMaESa8" + + "dG22CIMuIeRLVcAmEWEWH5EEforTg+QIDAQAB\n-----END PUBLIC KEY-----"; + private ConfigurableApplicationContext context; private ConfigurableEnvironment environment = new StandardEnvironment(); @@ -112,7 +122,7 @@ public class ResourceServerTokenServicesConfigurationTests { @Test public void asymmetricJwt() { EnvironmentTestUtils.addEnvironment(this.environment, - "spring.oauth2.resource.jwt.keyValue=" + publicKey); + "spring.oauth2.resource.jwt.keyValue=" + PUBLIC_KEY); this.context = new SpringApplicationBuilder(ResourceConfiguration.class) .environment(this.environment).web(false).run(); DefaultTokenServices services = this.context.getBean(DefaultTokenServices.class); @@ -141,6 +151,7 @@ public class ResourceServerTokenServicesConfigurationTests { PropertyPlaceholderAutoConfiguration.class }) @EnableConfigurationProperties(OAuth2ClientProperties.class) protected static class ResourceConfiguration { + } @Configuration @@ -154,18 +165,17 @@ public class ResourceServerTokenServicesConfigurationTests { return new ResourceServerProperties(this.credentials.getClientId(), this.credentials.getClientSecret()); } + } @Import({ FacebookAutoConfiguration.class, SocialWebAutoConfiguration.class }) protected static class SocialResourceConfiguration extends ResourceConfiguration { + @Bean public EmbeddedServletContainerFactory embeddedServletContainerFactory() { - return Mockito.mock(EmbeddedServletContainerFactory.class); + return mock(EmbeddedServletContainerFactory.class); } + } - private static String publicKey = "-----BEGIN PUBLIC KEY-----\n" - + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnGp/Q5lh0P8nPL21oMMrt2RrkT9AW5jgYwLfSUnJVc9G6uR3cXRRDCjHqWU5WYwivcF180A6CWp/ireQFFBNowgc5XaA0kPpzEtgsA5YsNX7iSnUibB004iBTfU9hZ2Rbsc8cWqynT0RyN4TP1RYVSeVKvMQk4GT1r7JCEC+TNu1ELmbNwMQyzKjsfBXyIOCFU/E94ktvsTZUHF4Oq44DBylCDsS1k7/sfZC2G5EU7Oz0mhG8+Uz6MSEQHtoIi6mc8u64Rwi3Z3tscuWG2ShtsUFuNSAFNkY7LkLn+/hxLCu2bNISMaESa8dG22CIMuIeRLVcAmEWEWH5EEforTg+QIDAQAB\n" - + "-----END PUBLIC KEY-----"; - } diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/resource/UserInfoTokenServicesTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/resource/UserInfoTokenServicesTests.java index e218e7c4a1..84a183ceb2 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/resource/UserInfoTokenServicesTests.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/resource/UserInfoTokenServicesTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2014 the original author or authors. + * Copyright 2012-2015 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. @@ -15,14 +15,11 @@ */ package org.springframework.boot.autoconfigure.security.oauth2.resource; -import static org.junit.Assert.assertEquals; - import java.util.LinkedHashMap; import java.util.Map; import org.junit.Before; import org.junit.Test; -import org.mockito.Mockito; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.security.oauth2.client.OAuth2ClientContext; @@ -30,42 +27,51 @@ import org.springframework.security.oauth2.client.OAuth2RestOperations; import org.springframework.security.oauth2.client.resource.BaseOAuth2ProtectedResourceDetails; import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken; +import static org.junit.Assert.assertEquals; +import static org.mockito.BDDMockito.given; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.mock; + /** - * @author Dave Syer + * Tests for {@link UserInfoTokenServices}. * + * @author Dave Syer */ public class UserInfoTokenServicesTests { private UserInfoTokenServices services = new UserInfoTokenServices( "http://example.com", "foo"); + private BaseOAuth2ProtectedResourceDetails resource = new BaseOAuth2ProtectedResourceDetails(); - private OAuth2RestOperations template = Mockito.mock(OAuth2RestOperations.class); + + private OAuth2RestOperations template = mock(OAuth2RestOperations.class); + private Map map = new LinkedHashMap(); @Before @SuppressWarnings({ "unchecked", "rawtypes" }) public void init() { - resource.setClientId("foo"); - Mockito.when( - template.getForEntity(Mockito.any(String.class), Mockito.any(Class.class))) - .thenReturn(new ResponseEntity(map, HttpStatus.OK)); - Mockito.when(template.getAccessToken()).thenReturn(new DefaultOAuth2AccessToken("FOO")); - Mockito.when(template.getResource()).thenReturn(resource); - Mockito.when(template.getOAuth2ClientContext()).thenReturn( - Mockito.mock(OAuth2ClientContext.class)); + this.resource.setClientId("foo"); + given(this.template.getForEntity(any(String.class), any(Class.class))) + .willReturn(new ResponseEntity(this.map, HttpStatus.OK)); + given(this.template.getAccessToken()).willReturn( + new DefaultOAuth2AccessToken("FOO")); + given(this.template.getResource()).willReturn(this.resource); + given(this.template.getOAuth2ClientContext()).willReturn( + mock(OAuth2ClientContext.class)); } @Test public void sunnyDay() { - services.setRestTemplate(template); - assertEquals("unknown", services.loadAuthentication("FOO").getName()); + this.services.setRestTemplate(this.template); + assertEquals("unknown", this.services.loadAuthentication("FOO").getName()); } @Test public void userId() { - map.put("userid", "spencer"); - services.setRestTemplate(template); - assertEquals("spencer", services.loadAuthentication("FOO").getName()); + this.map.put("userid", "spencer"); + this.services.setRestTemplate(this.template); + assertEquals("spencer", this.services.loadAuthentication("FOO").getName()); } } diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/sso/BasicOAuth2SsoConfigurationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/sso/BasicOAuth2SsoConfigurationTests.java index 5131906f42..9f15181b97 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/sso/BasicOAuth2SsoConfigurationTests.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/sso/BasicOAuth2SsoConfigurationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2014 the original author or authors. + * Copyright 2012-2014 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. @@ -13,11 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.boot.autoconfigure.security.oauth2.sso; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +package org.springframework.boot.autoconfigure.security.oauth2.sso; import javax.servlet.Filter; @@ -39,9 +36,14 @@ import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.web.context.WebApplicationContext; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + /** - * @author Dave Syer + * Tests for {@link OAuth2AutoConfiguration} with basic configuration. * + * @author Dave Syer */ @RunWith(SpringJUnit4ClassRunner.class) @SpringApplicationConfiguration(classes = TestConfiguration.class) @@ -64,12 +66,13 @@ public class BasicOAuth2SsoConfigurationTests { @Before public void init() { - mvc = MockMvcBuilders.webAppContextSetup(context).addFilters(filter).build(); + this.mvc = MockMvcBuilders.webAppContextSetup(this.context) + .addFilters(this.filter).build(); } @Test public void homePageIsSecure() throws Exception { - mvc.perform(get("/")).andExpect(status().isFound()) + this.mvc.perform(get("/")).andExpect(status().isFound()) .andExpect(header().string("location", "http://localhost/login")); } diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/sso/CustomOAuth2SsoConfigurationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/sso/CustomOAuth2SsoConfigurationTests.java index e468a11330..4fd6d75eff 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/sso/CustomOAuth2SsoConfigurationTests.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/sso/CustomOAuth2SsoConfigurationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2014 the original author or authors. + * Copyright 2012-2015 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. @@ -13,13 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.boot.autoconfigure.security.oauth2.sso; -import static org.hamcrest.Matchers.startsWith; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +package org.springframework.boot.autoconfigure.security.oauth2.sso; import javax.servlet.Filter; @@ -45,9 +40,16 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.context.WebApplicationContext; +import static org.hamcrest.Matchers.startsWith; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + /** - * @author Dave Syer + * Tests for {@link OAuth2AutoConfiguration} with custom configuration. * + * @author Dave Syer */ @RunWith(SpringJUnit4ClassRunner.class) @SpringApplicationConfiguration(classes = TestConfiguration.class) @@ -70,24 +72,25 @@ public class CustomOAuth2SsoConfigurationTests { @Before public void init() { - mvc = MockMvcBuilders.webAppContextSetup(context).addFilters(filter).build(); + this.mvc = MockMvcBuilders.webAppContextSetup(this.context) + .addFilters(this.filter).build(); } @Test public void homePageIsBasicAuth() throws Exception { - mvc.perform(get("/")).andExpect(status().isUnauthorized()) + this.mvc.perform(get("/")).andExpect(status().isUnauthorized()) .andExpect(header().string("WWW-Authenticate", startsWith("Basic"))); } @Test public void uiPageIsSecure() throws Exception { - mvc.perform(get("/ui/")).andExpect(status().isFound()) + this.mvc.perform(get("/ui/")).andExpect(status().isFound()) .andExpect(header().string("location", "http://localhost/login")); } @Test public void uiTestPageIsAccessible() throws Exception { - mvc.perform(get("/ui/test")).andExpect(status().isOk()) + this.mvc.perform(get("/ui/test")).andExpect(status().isOk()) .andExpect(content().string("test")); } @@ -112,6 +115,7 @@ public class CustomOAuth2SsoConfigurationTests { } } + } } diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/sso/MinimalSecureWebConfiguration.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/sso/MinimalSecureWebConfiguration.java index 27b0acd874..625265ab16 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/sso/MinimalSecureWebConfiguration.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/sso/MinimalSecureWebConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 the original author or authors. + * Copyright 2012-2015 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. @@ -40,8 +40,8 @@ import org.springframework.context.annotation.Import; @Import({ EmbeddedServletContainerAutoConfiguration.class, ServerPropertiesAutoConfiguration.class, DispatcherServletAutoConfiguration.class, WebMvcAutoConfiguration.class, - HttpMessageConvertersAutoConfiguration.class, - ErrorMvcAutoConfiguration.class, PropertyPlaceholderAutoConfiguration.class, - SecurityAutoConfiguration.class }) @interface MinimalSecureWebConfiguration { + HttpMessageConvertersAutoConfiguration.class, ErrorMvcAutoConfiguration.class, + PropertyPlaceholderAutoConfiguration.class, SecurityAutoConfiguration.class }) +public @interface MinimalSecureWebConfiguration { -} \ No newline at end of file +} diff --git a/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/autoconfigure/SpringSecurityOAuth2CompilerAutoConfiguration.java b/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/autoconfigure/SpringSecurityOAuth2CompilerAutoConfiguration.java index a255220dcc..8ee150dee4 100644 --- a/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/autoconfigure/SpringSecurityOAuth2CompilerAutoConfiguration.java +++ b/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/autoconfigure/SpringSecurityOAuth2CompilerAutoConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2014 the original author or authors. + * Copyright 2012-2015 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. @@ -28,28 +28,30 @@ import org.springframework.boot.cli.compiler.DependencyCustomizer; * * @author Greg Turnquist * @author Dave Syer + * @since 1.3.0 */ -public class SpringSecurityOAuth2CompilerAutoConfiguration extends CompilerAutoConfiguration { +public class SpringSecurityOAuth2CompilerAutoConfiguration extends + CompilerAutoConfiguration { @Override public boolean matches(ClassNode classNode) { - return AstUtils.hasAtLeastOneAnnotation(classNode, - "EnableAuthorizationServer", "EnableResourceServer", "EnableOAuth2Client", "EnableOAuth2Sso"); + return AstUtils.hasAtLeastOneAnnotation(classNode, "EnableAuthorizationServer", + "EnableResourceServer", "EnableOAuth2Client", "EnableOAuth2Sso"); } @Override - public void applyDependencies(DependencyCustomizer dependencies) throws CompilationFailedException { - dependencies.add("spring-security-oauth2").add("spring-boot-starter-web") - .add("spring-boot-starter-security"); + public void applyDependencies(DependencyCustomizer dependencies) + throws CompilationFailedException { + dependencies.add("spring-security-oauth2", "spring-boot-starter-web", + "spring-boot-starter-security"); } @Override public void applyImports(ImportCustomizer imports) throws CompilationFailedException { - imports - .addImports( - "org.springframework.boot.autoconfigure.security.oauth2.client.EnableOAuth2Sso") - .addStarImports( - "org.springframework.security.oauth2.config.annotation.web.configuration", - "org.springframework.security.access.prepost"); + imports.addImports("org.springframework.boot.autoconfigure.security.oauth2.client.EnableOAuth2Sso"); + imports.addStarImports( + "org.springframework.security.oauth2.config.annotation.web.configuration", + "org.springframework.security.access.prepost"); } + } diff --git a/spring-boot-cli/src/test/java/org/springframework/boot/cli/SampleIntegrationTests.java b/spring-boot-cli/src/test/java/org/springframework/boot/cli/SampleIntegrationTests.java index ceb301e2b2..7f823192c0 100644 --- a/spring-boot-cli/src/test/java/org/springframework/boot/cli/SampleIntegrationTests.java +++ b/spring-boot-cli/src/test/java/org/springframework/boot/cli/SampleIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2014 the original author or authors. + * Copyright 2012-2015 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. @@ -16,11 +16,6 @@ package org.springframework.boot.cli; -import static org.hamcrest.Matchers.containsString; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.assertTrue; - import java.io.File; import java.net.URI; @@ -28,6 +23,11 @@ import org.junit.Ignore; import org.junit.Rule; import org.junit.Test; +import static org.hamcrest.Matchers.containsString; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; + /** * Integration tests to exercise the samples. * @@ -72,8 +72,10 @@ public class SampleIntegrationTests { @Test public void oauth2Sample() throws Exception { String output = this.cli.run("oauth2.groovy"); - assertTrue("Wrong output: " + output, output.contains("spring.oauth2.client.clientId")); - assertTrue("Wrong output: " + output, output.contains("spring.oauth2.client.secret =")); + assertTrue("Wrong output: " + output, + output.contains("spring.oauth2.client.clientId")); + assertTrue("Wrong output: " + output, + output.contains("spring.oauth2.client.secret =")); } @Test diff --git a/spring-boot-samples/spring-boot-sample-secure-oauth2/src/main/java/sample/Flight.java b/spring-boot-samples/spring-boot-sample-secure-oauth2/src/main/java/sample/secure/oauth2/Flight.java similarity index 85% rename from spring-boot-samples/spring-boot-sample-secure-oauth2/src/main/java/sample/Flight.java rename to spring-boot-samples/spring-boot-sample-secure-oauth2/src/main/java/sample/secure/oauth2/Flight.java index 0319074702..f83eb6f2ef 100644 --- a/spring-boot-samples/spring-boot-sample-secure-oauth2/src/main/java/sample/Flight.java +++ b/spring-boot-samples/spring-boot-sample-secure-oauth2/src/main/java/sample/secure/oauth2/Flight.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2014 the original author or authors. + * Copyright 2012-2015 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. @@ -13,13 +13,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package sample; + +package sample.secure.oauth2; + +import java.util.Date; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; -import java.util.Date; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; @@ -33,18 +35,24 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; @JsonIgnoreProperties(ignoreUnknown = true) public class Flight { - @Id @GeneratedValue(strategy = GenerationType.AUTO) + @Id + @GeneratedValue(strategy = GenerationType.AUTO) private Long id; private String origin; + private String destination; + private String airline; + private String flightNumber; + private Date date; + private String traveler; public Long getId() { - return id; + return this.id; } public void setId(Long id) { @@ -52,7 +60,7 @@ public class Flight { } public String getOrigin() { - return origin; + return this.origin; } public void setOrigin(String origin) { @@ -60,7 +68,7 @@ public class Flight { } public String getDestination() { - return destination; + return this.destination; } public void setDestination(String destination) { @@ -68,7 +76,7 @@ public class Flight { } public String getAirline() { - return airline; + return this.airline; } public void setAirline(String airline) { @@ -76,7 +84,7 @@ public class Flight { } public String getFlightNumber() { - return flightNumber; + return this.flightNumber; } public void setFlightNumber(String flightNumber) { @@ -84,7 +92,7 @@ public class Flight { } public Date getDate() { - return date; + return this.date; } public void setDate(Date date) { @@ -92,10 +100,11 @@ public class Flight { } public String getTraveler() { - return traveler; + return this.traveler; } public void setTraveler(String traveler) { this.traveler = traveler; } + } diff --git a/spring-boot-samples/spring-boot-sample-secure-oauth2/src/main/java/sample/FlightRepository.java b/spring-boot-samples/spring-boot-sample-secure-oauth2/src/main/java/sample/secure/oauth2/FlightRepository.java similarity index 92% rename from spring-boot-samples/spring-boot-sample-secure-oauth2/src/main/java/sample/FlightRepository.java rename to spring-boot-samples/spring-boot-sample-secure-oauth2/src/main/java/sample/secure/oauth2/FlightRepository.java index 811b062af5..96390d7c20 100644 --- a/spring-boot-samples/spring-boot-sample-secure-oauth2/src/main/java/sample/FlightRepository.java +++ b/spring-boot-samples/spring-boot-sample-secure-oauth2/src/main/java/sample/secure/oauth2/FlightRepository.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2014 the original author or authors. + * Copyright 2012-2015 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. @@ -13,7 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package sample; + +package sample.secure.oauth2; import org.springframework.data.repository.CrudRepository; import org.springframework.security.access.prepost.PreAuthorize; @@ -26,15 +27,16 @@ import org.springframework.security.access.prepost.PreAuthorize; */ public interface FlightRepository extends CrudRepository { - @PreAuthorize("#oauth2.hasScope('read')") @Override + @PreAuthorize("#oauth2.hasScope('read')") Iterable findAll(); - @PreAuthorize("#oauth2.hasScope('read')") @Override + @PreAuthorize("#oauth2.hasScope('read')") Flight findOne(Long aLong); - @PreAuthorize("#oauth2.hasScope('write')") @Override + @PreAuthorize("#oauth2.hasScope('write')") S save(S entity); + } diff --git a/spring-boot-samples/spring-boot-sample-secure-oauth2/src/main/java/sample/Application.java b/spring-boot-samples/spring-boot-sample-secure-oauth2/src/main/java/sample/secure/oauth2/SampleSecureOAuth2Application.java similarity index 94% rename from spring-boot-samples/spring-boot-sample-secure-oauth2/src/main/java/sample/Application.java rename to spring-boot-samples/spring-boot-sample-secure-oauth2/src/main/java/sample/secure/oauth2/SampleSecureOAuth2Application.java index df61dd3519..0f45be8151 100644 --- a/spring-boot-samples/spring-boot-sample-secure-oauth2/src/main/java/sample/Application.java +++ b/spring-boot-samples/spring-boot-sample-secure-oauth2/src/main/java/sample/secure/oauth2/SampleSecureOAuth2Application.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2014 the original author or authors. + * Copyright 2012-2015 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. @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package sample; +package sample.secure.oauth2; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @@ -21,14 +21,11 @@ import org.springframework.security.config.annotation.method.configuration.Enabl import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer; import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer; -// @formatter:off /** * After you launch the app, you can seek a bearer token like this: * *
- *
  * curl localhost:8080/oauth/token -d "grant_type=password&scope=read&username=greg&password=turnquist" -u foo:bar
- *
  * 
* *
    @@ -91,16 +88,14 @@ import org.springframework.security.oauth2.config.annotation.web.configuration.E * @author Craig Walls * @author Greg Turnquist */ -// @formatter:on - @SpringBootApplication @EnableAuthorizationServer @EnableResourceServer @EnableGlobalMethodSecurity(prePostEnabled = true) -public class Application { +public class SampleSecureOAuth2Application { public static void main(String[] args) { - SpringApplication.run(Application.class, args); + SpringApplication.run(SampleSecureOAuth2Application.class, args); } } diff --git a/spring-boot-samples/spring-boot-sample-secure-oauth2/src/test/java/sample/ApplicationTests.java b/spring-boot-samples/spring-boot-sample-secure-oauth2/src/test/java/sample/secure/oauth2/SampleSecureOAuth2ApplicationTests.java similarity index 63% rename from spring-boot-samples/spring-boot-sample-secure-oauth2/src/test/java/sample/ApplicationTests.java rename to spring-boot-samples/spring-boot-sample-secure-oauth2/src/test/java/sample/secure/oauth2/SampleSecureOAuth2ApplicationTests.java index 5451b516ce..b12a7e087e 100644 --- a/spring-boot-samples/spring-boot-sample-secure-oauth2/src/test/java/sample/ApplicationTests.java +++ b/spring-boot-samples/spring-boot-sample-secure-oauth2/src/test/java/sample/secure/oauth2/SampleSecureOAuth2ApplicationTests.java @@ -1,13 +1,4 @@ -package sample; - -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; -import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; -import static org.springframework.test.web.servlet.setup.MockMvcBuilders.webAppContextSetup; +package sample.secure.oauth2; import java.util.Map; @@ -28,8 +19,20 @@ import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.MvcResult; import org.springframework.web.context.WebApplicationContext; +import sample.secure.oauth2.SampleSecureOAuth2Application; +import sample.secure.oauth2.Flight; + import com.fasterxml.jackson.databind.ObjectMapper; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import static org.springframework.test.web.servlet.setup.MockMvcBuilders.webAppContextSetup; + /** * Series of automated integration tests to verify proper behavior of auto-configured, * OAuth2-secured system @@ -38,12 +41,13 @@ import com.fasterxml.jackson.databind.ObjectMapper; */ @RunWith(SpringJUnit4ClassRunner.class) @WebAppConfiguration -@SpringApplicationConfiguration(classes = Application.class) +@SpringApplicationConfiguration(classes = SampleSecureOAuth2Application.class) @IntegrationTest("server.port:0") -public class ApplicationTests { +public class SampleSecureOAuth2ApplicationTests { @Autowired WebApplicationContext context; + @Autowired FilterChainProxy filterChain; @@ -53,85 +57,51 @@ public class ApplicationTests { @Before public void setUp() { - this.mvc = webAppContextSetup(this.context).addFilters(this.filterChain).build(); SecurityContextHolder.clearContext(); } @Test public void everythingIsSecuredByDefault() throws Exception { - - this.mvc.perform(get("/").// - accept(MediaTypes.HAL_JSON)).// / - andExpect(status().isUnauthorized()).// - andDo(print()); - - this.mvc.perform(get("/flights").// - accept(MediaTypes.HAL_JSON)).// / - andExpect(status().isUnauthorized()).// - andDo(print()); - - this.mvc.perform(get("/flights/1").// - accept(MediaTypes.HAL_JSON)).// / - andExpect(status().isUnauthorized()).// - andDo(print()); - - this.mvc.perform(get("/alps").// - accept(MediaTypes.HAL_JSON)).// / - andExpect(status().isUnauthorized()).// - andDo(print()); + this.mvc.perform(get("/").accept(MediaTypes.HAL_JSON)) + .andExpect(status().isUnauthorized()).andDo(print()); + this.mvc.perform(get("/flights").accept(MediaTypes.HAL_JSON)) + .andExpect(status().isUnauthorized()).andDo(print()); + this.mvc.perform(get("/flights/1").accept(MediaTypes.HAL_JSON)) + .andExpect(status().isUnauthorized()).andDo(print()); + this.mvc.perform(get("/alps").accept(MediaTypes.HAL_JSON)) + .andExpect(status().isUnauthorized()).andDo(print()); } @Test @Ignore - // TODO: maybe show mixed basic + token auth on different resources? public void accessingRootUriPossibleWithUserAccount() throws Exception { - + String header = "Basic " + new String(Base64.encode("greg:turnquist".getBytes())); this.mvc.perform( - get("/").// - accept(MediaTypes.HAL_JSON).// - header("Authorization", - "Basic " - + new String(Base64.encode("greg:turnquist" - .getBytes())))) - .// - andExpect(header().string("Content-Type", MediaTypes.HAL_JSON.toString())) - .// - andExpect(status().isOk()).// - andDo(print()); + get("/").accept(MediaTypes.HAL_JSON).header("Authorization", header)) + .andExpect( + header().string("Content-Type", MediaTypes.HAL_JSON.toString())) + .andExpect(status().isOk()).andDo(print()); } @Test public void useAppSecretsPlusUserAccountToGetBearerToken() throws Exception { - - // @formatter:off + String header = "Basic " + new String(Base64.encode("foo:bar".getBytes())); MvcResult result = this.mvc .perform( - post("/oauth/token"). - header("Authorization", - "Basic " + new String(Base64.encode("foo:bar".getBytes()))). - param("grant_type", "password"). - param("scope", "read"). - param("username", "greg"). - param("password", "turnquist")). - andExpect(status().isOk()). - andDo(print()). - andReturn(); - // @formatter:on - + post("/oauth/token").header("Authorization", header) + .param("grant_type", "password").param("scope", "read") + .param("username", "greg").param("password", "turnquist")) + .andExpect(status().isOk()).andDo(print()).andReturn(); Object accessToken = this.objectMapper.readValue( result.getResponse().getContentAsString(), Map.class).get("access_token"); - MvcResult flightsAction = this.mvc - .perform(get("/flights/1").// - accept(MediaTypes.HAL_JSON).// - header("Authorization", "Bearer " + accessToken)) - .// - andExpect(header().string("Content-Type", MediaTypes.HAL_JSON.toString())) - .// - andExpect(status().isOk()).// - andDo(print()).// - andReturn(); + .perform( + get("/flights/1").accept(MediaTypes.HAL_JSON).header( + "Authorization", "Bearer " + accessToken)) + .andExpect( + header().string("Content-Type", MediaTypes.HAL_JSON.toString())) + .andExpect(status().isOk()).andDo(print()).andReturn(); Flight flight = this.objectMapper.readValue(flightsAction.getResponse() .getContentAsString(), Flight.class);