diff --git a/spring-boot-autoconfigure/pom.xml b/spring-boot-autoconfigure/pom.xml index 2970da817a..a8e7c79bd6 100755 --- a/spring-boot-autoconfigure/pom.xml +++ b/spring-boot-autoconfigure/pom.xml @@ -522,16 +522,6 @@ spring-security-data true - - org.springframework.security.oauth - spring-security-oauth2 - true - - - org.springframework.security - spring-security-jwt - true - org.springframework.session spring-session-core 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 deleted file mode 100644 index aee53ce9f0..0000000000 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/OAuth2AutoConfiguration.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright 2012-2017 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.boot.autoconfigure.security.oauth2; - -import org.springframework.boot.autoconfigure.AutoConfigureBefore; -import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; -import org.springframework.boot.autoconfigure.security.oauth2.authserver.OAuth2AuthorizationServerConfiguration; -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.OAuth2ResourceServerConfiguration; -import org.springframework.boot.autoconfigure.security.oauth2.resource.ResourceServerProperties; -import org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration; -import org.springframework.boot.context.properties.EnableConfigurationProperties; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Import; -import org.springframework.security.oauth2.common.OAuth2AccessToken; -import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; - -/** - * {@link EnableAutoConfiguration Auto-configuration} for Spring Security OAuth2. - * - * @author Greg Turnquist - * @author Dave Syer - * @since 1.3.0 - */ -@Configuration -@ConditionalOnClass({ OAuth2AccessToken.class, WebMvcConfigurer.class }) -@Import({ OAuth2AuthorizationServerConfiguration.class, - OAuth2MethodSecurityConfiguration.class, OAuth2ResourceServerConfiguration.class, - OAuth2RestOperationsConfiguration.class }) -@AutoConfigureBefore(WebMvcAutoConfiguration.class) -@EnableConfigurationProperties(OAuth2ClientProperties.class) -public class OAuth2AutoConfiguration { - - private final OAuth2ClientProperties credentials; - - public OAuth2AutoConfiguration(OAuth2ClientProperties credentials) { - this.credentials = credentials; - } - - @Bean - public ResourceServerProperties resourceServerProperties() { - return new ResourceServerProperties(this.credentials.getClientId(), - this.credentials.getClientSecret()); - } - -} 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 deleted file mode 100644 index 00cd9da9e2..0000000000 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/OAuth2ClientProperties.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright 2012-2017 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.boot.autoconfigure.security.oauth2; - -import java.util.UUID; - -import org.springframework.boot.context.properties.ConfigurationProperties; - -/** - * Configuration properties for OAuth2 Client. - * - * @author Dave Syer - * @author Stephane Nicoll - * @since 1.3.0 - */ -@ConfigurationProperties(prefix = "security.oauth2.client") -public class OAuth2ClientProperties { - - /** - * OAuth2 client id. - */ - private String clientId; - - /** - * OAuth2 client secret. A random secret is generated by default. - */ - private String clientSecret = UUID.randomUUID().toString(); - - private boolean defaultSecret = true; - - public String getClientId() { - return this.clientId; - } - - public void setClientId(String clientId) { - this.clientId = clientId; - } - - public String getClientSecret() { - return this.clientSecret; - } - - public void setClientSecret(String clientSecret) { - this.clientSecret = clientSecret; - this.defaultSecret = false; - } - - public boolean isDefaultSecret() { - return this.defaultSecret; - } - -} diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/authserver/AuthorizationServerProperties.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/authserver/AuthorizationServerProperties.java deleted file mode 100644 index 214307fadf..0000000000 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/authserver/AuthorizationServerProperties.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright 2012-2017 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.boot.autoconfigure.security.oauth2.authserver; - -import org.springframework.boot.context.properties.ConfigurationProperties; - -/** - * Configuration properties for OAuth2 Authorization server. - * - * @author Dave Syer - * @since 1.3.0 - */ -@ConfigurationProperties(prefix = "security.oauth2.authorization") -public class AuthorizationServerProperties { - - /** - * Spring Security access rule for the check token endpoint (e.g. a SpEL expression - * like "isAuthenticated()") . Default is empty, which is interpreted as "denyAll()" - * (no access). - */ - private String checkTokenAccess; - - /** - * Spring Security access rule for the token key endpoint (e.g. a SpEL expression like - * "isAuthenticated()"). Default is empty, which is interpreted as "denyAll()" (no - * access). - */ - private String tokenKeyAccess; - - /** - * Realm name for client authentication. If an unauthenticated request comes in to the - * token endpoint, it will respond with a challenge including this name. - */ - private String realm; - - public String getCheckTokenAccess() { - return this.checkTokenAccess; - } - - public void setCheckTokenAccess(String checkTokenAccess) { - this.checkTokenAccess = checkTokenAccess; - } - - public String getTokenKeyAccess() { - return this.tokenKeyAccess; - } - - public void setTokenKeyAccess(String tokenKeyAccess) { - this.tokenKeyAccess = tokenKeyAccess; - } - - public String getRealm() { - return this.realm; - } - - public void setRealm(String realm) { - this.realm = realm; - } - -} diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/authserver/OAuth2AuthorizationServerConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/authserver/OAuth2AuthorizationServerConfiguration.java deleted file mode 100644 index fcb922a48c..0000000000 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/authserver/OAuth2AuthorizationServerConfiguration.java +++ /dev/null @@ -1,204 +0,0 @@ -/* - * Copyright 2012-2017 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.boot.autoconfigure.security.oauth2.authserver; - -import java.util.Arrays; -import java.util.Collections; -import java.util.UUID; - -import javax.annotation.PostConstruct; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import org.springframework.beans.factory.ObjectProvider; -import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; -import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; -import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; -import org.springframework.boot.autoconfigure.security.oauth2.OAuth2ClientProperties; -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.boot.context.properties.EnableConfigurationProperties; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.security.authentication.AuthenticationManager; -import org.springframework.security.core.authority.AuthorityUtils; -import org.springframework.security.oauth2.config.annotation.builders.ClientDetailsServiceBuilder; -import org.springframework.security.oauth2.config.annotation.builders.InMemoryClientDetailsServiceBuilder; -import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer; -import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurer; -import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter; -import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerEndpointsConfiguration; -import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer; -import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer; -import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer; -import org.springframework.security.oauth2.provider.client.BaseClientDetails; -import org.springframework.security.oauth2.provider.token.AccessTokenConverter; -import org.springframework.security.oauth2.provider.token.TokenStore; - -/** - * 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) -@ConditionalOnMissingBean(AuthorizationServerConfigurer.class) -@ConditionalOnBean(AuthorizationServerEndpointsConfiguration.class) -@EnableConfigurationProperties(AuthorizationServerProperties.class) -public class OAuth2AuthorizationServerConfiguration - extends AuthorizationServerConfigurerAdapter { - - private static final Log logger = LogFactory - .getLog(OAuth2AuthorizationServerConfiguration.class); - - private final BaseClientDetails details; - - private final AuthenticationManager authenticationManager; - - private final TokenStore tokenStore; - - private final AccessTokenConverter tokenConverter; - - private final AuthorizationServerProperties properties; - - public OAuth2AuthorizationServerConfiguration(BaseClientDetails details, - AuthenticationManager authenticationManager, - ObjectProvider tokenStore, - ObjectProvider tokenConverter, - AuthorizationServerProperties properties) { - this.details = details; - this.authenticationManager = authenticationManager; - this.tokenStore = tokenStore.getIfAvailable(); - this.tokenConverter = tokenConverter.getIfAvailable(); - this.properties = properties; - } - - @Override - public void configure(ClientDetailsServiceConfigurer clients) throws Exception { - ClientDetailsServiceBuilder.ClientBuilder builder = clients - .inMemory().withClient(this.details.getClientId()); - builder.secret(this.details.getClientSecret()) - .resourceIds(this.details.getResourceIds().toArray(new String[0])) - .authorizedGrantTypes( - this.details.getAuthorizedGrantTypes().toArray(new String[0])) - .authorities( - AuthorityUtils.authorityListToSet(this.details.getAuthorities()) - .toArray(new String[0])) - .scopes(this.details.getScope().toArray(new String[0])); - - if (this.details.getAutoApproveScopes() != null) { - builder.autoApprove( - this.details.getAutoApproveScopes().toArray(new String[0])); - } - if (this.details.getAccessTokenValiditySeconds() != null) { - builder.accessTokenValiditySeconds( - this.details.getAccessTokenValiditySeconds()); - } - if (this.details.getRefreshTokenValiditySeconds() != null) { - builder.refreshTokenValiditySeconds( - this.details.getRefreshTokenValiditySeconds()); - } - if (this.details.getRegisteredRedirectUri() != null) { - builder.redirectUris( - this.details.getRegisteredRedirectUri().toArray(new String[0])); - } - } - - @Override - public void configure(AuthorizationServerEndpointsConfigurer endpoints) - throws Exception { - if (this.tokenConverter != null) { - endpoints.accessTokenConverter(this.tokenConverter); - } - if (this.tokenStore != null) { - endpoints.tokenStore(this.tokenStore); - } - if (this.details.getAuthorizedGrantTypes().contains("password")) { - endpoints.authenticationManager(this.authenticationManager); - } - } - - @Override - public void configure(AuthorizationServerSecurityConfigurer security) - throws Exception { - if (this.properties.getCheckTokenAccess() != null) { - security.checkTokenAccess(this.properties.getCheckTokenAccess()); - } - if (this.properties.getTokenKeyAccess() != null) { - security.tokenKeyAccess(this.properties.getTokenKeyAccess()); - } - if (this.properties.getRealm() != null) { - security.realm(this.properties.getRealm()); - } - } - - @Configuration - protected static class ClientDetailsLogger { - - private final OAuth2ClientProperties credentials; - - protected ClientDetailsLogger(OAuth2ClientProperties credentials) { - this.credentials = credentials; - } - - @PostConstruct - public void init() { - String prefix = "security.oauth2.client"; - boolean defaultSecret = this.credentials.isDefaultSecret(); - logger.info(String.format( - "Initialized OAuth2 Client%n%n%s.client-id = %s%n" - + "%s.client-secret = %s%n%n", - prefix, this.credentials.getClientId(), prefix, - defaultSecret ? this.credentials.getClientSecret() : "****")); - } - - } - - @Configuration - @ConditionalOnMissingBean(BaseClientDetails.class) - protected static class BaseClientDetailsConfiguration { - - private final OAuth2ClientProperties client; - - protected BaseClientDetailsConfiguration(OAuth2ClientProperties client) { - this.client = client; - } - - @Bean - @ConfigurationProperties(prefix = "security.oauth2.client") - public BaseClientDetails oauth2ClientDetails() { - BaseClientDetails details = new BaseClientDetails(); - if (this.client.getClientId() == null) { - this.client.setClientId(UUID.randomUUID().toString()); - } - details.setClientId(this.client.getClientId()); - details.setClientSecret(this.client.getClientSecret()); - details.setAuthorizedGrantTypes(Arrays.asList("authorization_code", - "password", "client_credentials", "implicit", "refresh_token")); - details.setAuthorities( - AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_USER")); - details.setRegisteredRedirectUri(Collections.emptySet()); - return details; - } - - } - -} 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 deleted file mode 100644 index 6c291e7385..0000000000 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/client/EnableOAuth2Sso.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2012-2017 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.boot.autoconfigure.security.oauth2.client; - -import java.lang.annotation.Documented; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -import org.springframework.boot.autoconfigure.security.oauth2.resource.ResourceServerTokenServicesConfiguration; -import org.springframework.boot.context.properties.EnableConfigurationProperties; -import org.springframework.context.annotation.Import; -import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; -import org.springframework.security.oauth2.config.annotation.web.configuration.EnableOAuth2Client; - -/** - * Enable OAuth2 Single Sign On (SSO). If there is an existing - * {@link WebSecurityConfigurerAdapter} provided by the user and annotated with - * {@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. - * - * @author Dave Syer - * @since 1.3.0 - */ -@Target(ElementType.TYPE) -@Retention(RetentionPolicy.RUNTIME) -@Documented -@EnableOAuth2Client -@EnableConfigurationProperties(OAuth2SsoProperties.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/EnableOAuth2SsoCondition.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/client/EnableOAuth2SsoCondition.java deleted file mode 100644 index 2ccc4feaeb..0000000000 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/client/EnableOAuth2SsoCondition.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright 2012-2016 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.boot.autoconfigure.security.oauth2.client; - -import org.springframework.boot.autoconfigure.condition.ConditionMessage; -import org.springframework.boot.autoconfigure.condition.ConditionOutcome; -import org.springframework.boot.autoconfigure.condition.SpringBootCondition; -import org.springframework.context.annotation.ConditionContext; -import org.springframework.core.type.AnnotatedTypeMetadata; -import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; - -/** - * Condition that checks for {@link EnableOAuth2Sso} on a - * {@link WebSecurityConfigurerAdapter}. - * - * @author Dave Syer - */ -class EnableOAuth2SsoCondition extends SpringBootCondition { - - @Override - public ConditionOutcome getMatchOutcome(ConditionContext context, - AnnotatedTypeMetadata metadata) { - String[] enablers = context.getBeanFactory() - .getBeanNamesForAnnotation(EnableOAuth2Sso.class); - ConditionMessage.Builder message = ConditionMessage - .forCondition("@EnableOAuth2Sso Condition"); - for (String name : enablers) { - if (context.getBeanFactory().isTypeMatch(name, - WebSecurityConfigurerAdapter.class)) { - return ConditionOutcome.match(message - .found("@EnableOAuth2Sso annotation on WebSecurityConfigurerAdapter") - .items(name)); - } - } - return ConditionOutcome.noMatch(message.didNotFind( - "@EnableOAuth2Sso annotation " + "on any WebSecurityConfigurerAdapter") - .atAll()); - } - -} diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/client/OAuth2ProtectedResourceDetailsConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/client/OAuth2ProtectedResourceDetailsConfiguration.java deleted file mode 100644 index ef47ba15d1..0000000000 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/client/OAuth2ProtectedResourceDetailsConfiguration.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2012-2017 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.boot.autoconfigure.security.oauth2.client; - -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Primary; -import org.springframework.security.oauth2.client.token.grant.code.AuthorizationCodeResourceDetails; - -/** - * Shared {@link AuthorizationCodeResourceDetails} configuration. - * - * @author Stephane Nicoll - */ -@Configuration -class OAuth2ProtectedResourceDetailsConfiguration { - - @Bean - @ConfigurationProperties(prefix = "security.oauth2.client") - @Primary - public AuthorizationCodeResourceDetails oauth2RemoteResource() { - return new AuthorizationCodeResourceDetails(); - } - -} 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 deleted file mode 100644 index 75be00657e..0000000000 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/client/OAuth2RestOperationsConfiguration.java +++ /dev/null @@ -1,209 +0,0 @@ -/* - * Copyright 2012-2017 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.boot.autoconfigure.security.oauth2.client; - -import org.springframework.beans.factory.ObjectProvider; -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.boot.autoconfigure.condition.AnyNestedCondition; -import org.springframework.boot.autoconfigure.condition.ConditionMessage; -import org.springframework.boot.autoconfigure.condition.ConditionOutcome; -import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; -import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; -import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; -import org.springframework.boot.autoconfigure.condition.ConditionalOnNotWebApplication; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.boot.autoconfigure.condition.NoneNestedConditions; -import org.springframework.boot.autoconfigure.condition.SpringBootCondition; -import org.springframework.boot.autoconfigure.security.SecurityProperties; -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.boot.web.servlet.FilterRegistrationBean; -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.context.annotation.Import; -import org.springframework.context.annotation.Primary; -import org.springframework.context.annotation.Scope; -import org.springframework.context.annotation.ScopedProxyMode; -import org.springframework.core.type.AnnotatedTypeMetadata; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.security.oauth2.client.DefaultOAuth2ClientContext; -import org.springframework.security.oauth2.client.filter.OAuth2ClientContextFilter; -import org.springframework.security.oauth2.client.token.AccessTokenRequest; -import org.springframework.security.oauth2.client.token.DefaultAccessTokenRequest; -import org.springframework.security.oauth2.client.token.grant.client.ClientCredentialsResourceDetails; -import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken; -import org.springframework.security.oauth2.config.annotation.web.configuration.EnableOAuth2Client; -import org.springframework.security.oauth2.config.annotation.web.configuration.OAuth2ClientConfiguration; -import org.springframework.security.oauth2.provider.OAuth2Authentication; -import org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationDetails; -import org.springframework.util.StringUtils; - -/** - * Configuration for OAuth2 Single Sign On REST operations. - * - * @author Dave Syer - * @author Madhura Bhave - * @since 1.3.0 - */ -@Configuration -@ConditionalOnClass(EnableOAuth2Client.class) -public class OAuth2RestOperationsConfiguration { - - @Configuration - @Conditional(ClientCredentialsCondition.class) - protected static class SingletonScopedConfiguration { - - @Bean - @ConfigurationProperties(prefix = "security.oauth2.client") - @Primary - public ClientCredentialsResourceDetails oauth2RemoteResource() { - ClientCredentialsResourceDetails details = new ClientCredentialsResourceDetails(); - return details; - } - - @Bean - public DefaultOAuth2ClientContext oauth2ClientContext() { - return new DefaultOAuth2ClientContext(new DefaultAccessTokenRequest()); - } - - } - - @Configuration - @ConditionalOnBean(OAuth2ClientConfiguration.class) - @Conditional({ OAuth2ClientIdCondition.class, NoClientCredentialsCondition.class }) - @Import(OAuth2ProtectedResourceDetailsConfiguration.class) - protected static class SessionScopedConfiguration { - - @Bean - public FilterRegistrationBean oauth2ClientFilterRegistration( - OAuth2ClientContextFilter filter, SecurityProperties security) { - FilterRegistrationBean registration = new FilterRegistrationBean<>(); - registration.setFilter(filter); - registration.setOrder(security.getFilter().getOrder() - 10); - return registration; - } - - @Configuration - protected static class ClientContextConfiguration { - - private final AccessTokenRequest accessTokenRequest; - - public ClientContextConfiguration( - @Qualifier("accessTokenRequest") ObjectProvider accessTokenRequest) { - this.accessTokenRequest = accessTokenRequest.getIfAvailable(); - } - - @Bean - @Scope(value = "session", proxyMode = ScopedProxyMode.INTERFACES) - public DefaultOAuth2ClientContext oauth2ClientContext() { - return new DefaultOAuth2ClientContext(this.accessTokenRequest); - } - - } - - } - - // When the authentication is per cookie but the stored token is an oauth2 one, we can - // pass that on to a client that wants to call downstream. We don't even need an - // OAuth2ClientContextFilter until we need to refresh the access token. To handle - // refresh tokens you need to @EnableOAuth2Client - @Configuration - @ConditionalOnMissingBean(OAuth2ClientConfiguration.class) - @Conditional({ OAuth2ClientIdCondition.class, NoClientCredentialsCondition.class }) - @Import(OAuth2ProtectedResourceDetailsConfiguration.class) - protected static class RequestScopedConfiguration { - - @Bean - @Scope(value = "request", proxyMode = ScopedProxyMode.INTERFACES) - public DefaultOAuth2ClientContext oauth2ClientContext() { - DefaultOAuth2ClientContext context = new DefaultOAuth2ClientContext( - new DefaultAccessTokenRequest()); - Authentication principal = SecurityContextHolder.getContext() - .getAuthentication(); - if (principal instanceof OAuth2Authentication) { - OAuth2Authentication authentication = (OAuth2Authentication) principal; - Object details = authentication.getDetails(); - if (details instanceof OAuth2AuthenticationDetails) { - OAuth2AuthenticationDetails oauthsDetails = (OAuth2AuthenticationDetails) details; - String token = oauthsDetails.getTokenValue(); - context.setAccessToken(new DefaultOAuth2AccessToken(token)); - } - } - return context; - } - - } - - /** - * Condition to check if a {@code security.oauth2.client.client-id} is specified. - */ - static class OAuth2ClientIdCondition extends SpringBootCondition { - - @Override - public ConditionOutcome getMatchOutcome(ConditionContext context, - AnnotatedTypeMetadata metadata) { - String clientId = context.getEnvironment() - .getProperty("security.oauth2.client.client-id"); - ConditionMessage.Builder message = ConditionMessage - .forCondition("OAuth Client ID"); - if (StringUtils.hasLength(clientId)) { - return ConditionOutcome.match(message - .foundExactly("security.oauth2.client.client-id property")); - } - return ConditionOutcome.noMatch(message - .didNotFind("security.oauth2.client.client-id property").atAll()); - } - - } - - /** - * Condition to check for no client credentials. - */ - static class NoClientCredentialsCondition extends NoneNestedConditions { - - NoClientCredentialsCondition() { - super(ConfigurationPhase.PARSE_CONFIGURATION); - } - - @Conditional(ClientCredentialsCondition.class) - static class ClientCredentialsActivated { - } - - } - - /** - * Condition to check for client credentials. - */ - static class ClientCredentialsCondition extends AnyNestedCondition { - - ClientCredentialsCondition() { - super(ConfigurationPhase.PARSE_CONFIGURATION); - } - - @ConditionalOnProperty(prefix = "security.oauth2.client", name = "grant-type", havingValue = "client_credentials", matchIfMissing = false) - static class ClientCredentialsConfigured { - } - - @ConditionalOnNotWebApplication - static class NoWebApplication { - } - - } - -} 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 deleted file mode 100644 index 7b5bb69c0f..0000000000 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/client/OAuth2SsoCustomConfiguration.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright 2012-2016 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.boot.autoconfigure.security.oauth2.client; - -import java.lang.reflect.Method; - -import org.aopalliance.intercept.MethodInterceptor; -import org.aopalliance.intercept.MethodInvocation; - -import org.springframework.aop.framework.ProxyFactory; -import org.springframework.beans.BeansException; -import org.springframework.beans.factory.config.BeanPostProcessor; -import org.springframework.context.ApplicationContext; -import org.springframework.context.ApplicationContextAware; -import org.springframework.context.annotation.Conditional; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.ImportAware; -import org.springframework.core.type.AnnotationMetadata; -import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; -import org.springframework.util.ClassUtils; -import org.springframework.util.ReflectionUtils; - -/** - * Configuration for OAuth2 Single Sign On (SSO) when there is an existing - * {@link WebSecurityConfigurerAdapter} provided by the user and annotated with - * {@code @EnableOAuth2Sso}. The user-provided configuration is enhanced by adding an - * authentication filter and an authentication entry point. - * - * @author Dave Syer - */ -@Configuration -@Conditional(EnableOAuth2SsoCondition.class) -public class OAuth2SsoCustomConfiguration - implements ImportAware, BeanPostProcessor, ApplicationContextAware { - - private Class configType; - - private ApplicationContext applicationContext; - - @Override - public void setApplicationContext(ApplicationContext applicationContext) { - this.applicationContext = applicationContext; - } - - @Override - public void setImportMetadata(AnnotationMetadata importMetadata) { - this.configType = ClassUtils.resolveClassName(importMetadata.getClassName(), - null); - - } - - @Override - public Object postProcessBeforeInitialization(Object bean, String beanName) - throws BeansException { - return bean; - } - - @Override - public Object postProcessAfterInitialization(Object bean, String beanName) - throws BeansException { - if (this.configType.isAssignableFrom(bean.getClass()) - && bean instanceof WebSecurityConfigurerAdapter) { - ProxyFactory factory = new ProxyFactory(); - factory.setTarget(bean); - factory.addAdvice(new SsoSecurityAdapter(this.applicationContext)); - bean = factory.getProxy(); - } - return bean; - } - - private static class SsoSecurityAdapter implements MethodInterceptor { - - private SsoSecurityConfigurer configurer; - - SsoSecurityAdapter(ApplicationContext applicationContext) { - this.configurer = new SsoSecurityConfigurer(applicationContext); - } - - @Override - public Object invoke(MethodInvocation invocation) throws Throwable { - if (invocation.getMethod().getName().equals("init")) { - Method method = ReflectionUtils - .findMethod(WebSecurityConfigurerAdapter.class, "getHttp"); - ReflectionUtils.makeAccessible(method); - HttpSecurity http = (HttpSecurity) ReflectionUtils.invokeMethod(method, - invocation.getThis()); - this.configurer.configure(http); - } - return invocation.proceed(); - } - - } - -} 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 deleted file mode 100644 index 09c90a1e8d..0000000000 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/client/OAuth2SsoDefaultConfiguration.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright 2012-2017 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.boot.autoconfigure.security.oauth2.client; - -import org.springframework.boot.autoconfigure.condition.ConditionOutcome; -import org.springframework.boot.autoconfigure.security.oauth2.client.OAuth2SsoDefaultConfiguration.NeedsWebSecurityCondition; -import org.springframework.context.ApplicationContext; -import org.springframework.context.annotation.ConditionContext; -import org.springframework.context.annotation.Conditional; -import org.springframework.context.annotation.Configuration; -import org.springframework.core.type.AnnotatedTypeMetadata; -import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; - -/** - * 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. - * - * @author Dave Syer - * @since 1.3.0 - */ -@Configuration -@Conditional(NeedsWebSecurityCondition.class) -public class OAuth2SsoDefaultConfiguration extends WebSecurityConfigurerAdapter { - - private final ApplicationContext applicationContext; - - public OAuth2SsoDefaultConfiguration(ApplicationContext applicationContext) { - this.applicationContext = applicationContext; - } - - @Override - protected void configure(HttpSecurity http) throws Exception { - http.antMatcher("/**").authorizeRequests().anyRequest().authenticated(); - new SsoSecurityConfigurer(this.applicationContext).configure(http); - } - - protected static class NeedsWebSecurityCondition extends EnableOAuth2SsoCondition { - - @Override - public ConditionOutcome getMatchOutcome(ConditionContext context, - AnnotatedTypeMetadata metadata) { - return ConditionOutcome.inverse(super.getMatchOutcome(context, metadata)); - } - - } - -} 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 deleted file mode 100644 index 37a290ddde..0000000000 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/client/OAuth2SsoProperties.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2012-2017 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.boot.autoconfigure.security.oauth2.client; - -import org.springframework.boot.context.properties.ConfigurationProperties; - -/** - * Configuration properties for OAuth2 Single Sign On (SSO). - * - * @author Dave Syer - * @since 1.3.0 - */ -@ConfigurationProperties(prefix = "security.oauth2.sso") -public class OAuth2SsoProperties { - - public static final String DEFAULT_LOGIN_PATH = "/login"; - - /** - * Path to the login page, i.e. the one that triggers the redirect to the OAuth2 - * Authorization Server. - */ - private String loginPath = DEFAULT_LOGIN_PATH; - - public String getLoginPath() { - return this.loginPath; - } - - public void setLoginPath(String loginPath) { - this.loginPath = loginPath; - } - -} 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 deleted file mode 100644 index 457463d8e9..0000000000 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/client/SsoSecurityConfigurer.java +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright 2012-2016 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.boot.autoconfigure.security.oauth2.client; - -import java.util.Collections; - -import org.springframework.boot.autoconfigure.security.oauth2.resource.UserInfoRestTemplateFactory; -import org.springframework.context.ApplicationContext; -import org.springframework.http.HttpStatus; -import org.springframework.http.MediaType; -import org.springframework.security.config.annotation.SecurityConfigurerAdapter; -import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.config.annotation.web.configurers.ExceptionHandlingConfigurer; -import org.springframework.security.oauth2.client.OAuth2RestOperations; -import org.springframework.security.oauth2.client.filter.OAuth2ClientAuthenticationProcessingFilter; -import org.springframework.security.oauth2.provider.token.ResourceServerTokenServices; -import org.springframework.security.web.DefaultSecurityFilterChain; -import org.springframework.security.web.authentication.HttpStatusEntryPoint; -import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint; -import org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter; -import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy; -import org.springframework.security.web.util.matcher.MediaTypeRequestMatcher; -import org.springframework.security.web.util.matcher.RequestHeaderRequestMatcher; -import org.springframework.web.accept.ContentNegotiationStrategy; -import org.springframework.web.accept.HeaderContentNegotiationStrategy; - -/** - * Configurer for OAuth2 Single Sign On (SSO). - * - * @author Dave Syer - */ -class SsoSecurityConfigurer { - - private ApplicationContext applicationContext; - - SsoSecurityConfigurer(ApplicationContext applicationContext) { - this.applicationContext = applicationContext; - } - - public void configure(HttpSecurity http) throws Exception { - OAuth2SsoProperties sso = this.applicationContext - .getBean(OAuth2SsoProperties.class); - // Delay the processing of the filter until we know the - // SessionAuthenticationStrategy is available: - http.apply(new OAuth2ClientAuthenticationConfigurer(oauth2SsoFilter(sso))); - addAuthenticationEntryPoint(http, sso); - } - - private void addAuthenticationEntryPoint(HttpSecurity http, OAuth2SsoProperties sso) - throws Exception { - ExceptionHandlingConfigurer exceptions = http.exceptionHandling(); - ContentNegotiationStrategy contentNegotiationStrategy = http - .getSharedObject(ContentNegotiationStrategy.class); - if (contentNegotiationStrategy == null) { - contentNegotiationStrategy = new HeaderContentNegotiationStrategy(); - } - MediaTypeRequestMatcher preferredMatcher = new MediaTypeRequestMatcher( - contentNegotiationStrategy, MediaType.APPLICATION_XHTML_XML, - new MediaType("image", "*"), MediaType.TEXT_HTML, MediaType.TEXT_PLAIN); - preferredMatcher.setIgnoredMediaTypes(Collections.singleton(MediaType.ALL)); - exceptions.defaultAuthenticationEntryPointFor( - new LoginUrlAuthenticationEntryPoint(sso.getLoginPath()), - preferredMatcher); - // When multiple entry points are provided the default is the first one - exceptions.defaultAuthenticationEntryPointFor( - new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED), - new RequestHeaderRequestMatcher("X-Requested-With", "XMLHttpRequest")); - } - - private OAuth2ClientAuthenticationProcessingFilter oauth2SsoFilter( - OAuth2SsoProperties sso) { - OAuth2RestOperations restTemplate = this.applicationContext - .getBean(UserInfoRestTemplateFactory.class).getUserInfoRestTemplate(); - ResourceServerTokenServices tokenServices = this.applicationContext - .getBean(ResourceServerTokenServices.class); - OAuth2ClientAuthenticationProcessingFilter filter = new OAuth2ClientAuthenticationProcessingFilter( - sso.getLoginPath()); - filter.setRestTemplate(restTemplate); - filter.setTokenServices(tokenServices); - filter.setApplicationEventPublisher(this.applicationContext); - return filter; - } - - private static class OAuth2ClientAuthenticationConfigurer - extends SecurityConfigurerAdapter { - - private OAuth2ClientAuthenticationProcessingFilter filter; - - OAuth2ClientAuthenticationConfigurer( - OAuth2ClientAuthenticationProcessingFilter filter) { - this.filter = filter; - } - - @Override - public void configure(HttpSecurity builder) throws Exception { - OAuth2ClientAuthenticationProcessingFilter ssoFilter = this.filter; - ssoFilter.setSessionAuthenticationStrategy( - builder.getSharedObject(SessionAuthenticationStrategy.class)); - builder.addFilterAfter(ssoFilter, - AbstractPreAuthenticatedProcessingFilter.class); - } - - } - -} 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 deleted file mode 100644 index 223b9e1e2e..0000000000 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/method/OAuth2MethodSecurityConfiguration.java +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright 2012-2017 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.boot.autoconfigure.security.oauth2.method; - -import org.springframework.beans.BeansException; -import org.springframework.beans.factory.BeanFactoryUtils; -import org.springframework.beans.factory.config.BeanFactoryPostProcessor; -import org.springframework.beans.factory.config.BeanPostProcessor; -import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; -import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; -import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; -import org.springframework.context.ApplicationContext; -import org.springframework.context.ApplicationContextAware; -import org.springframework.context.annotation.Configuration; -import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler; -import org.springframework.security.authentication.AuthenticationTrustResolver; -import org.springframework.security.config.annotation.method.configuration.GlobalMethodSecurityConfiguration; -import org.springframework.security.oauth2.common.OAuth2AccessToken; -import org.springframework.security.oauth2.provider.expression.OAuth2MethodSecurityExpressionHandler; - -/** - * Auto-configure an expression handler for method-level security (if the user already has - * {@code @EnableGlobalMethodSecurity}). - * - * @author Greg Turnquist - * @author Dave Syer - * @since 1.3.0 - */ -@Configuration -@ConditionalOnClass({ OAuth2AccessToken.class }) -@ConditionalOnBean(GlobalMethodSecurityConfiguration.class) -public class OAuth2MethodSecurityConfiguration - implements BeanFactoryPostProcessor, ApplicationContextAware { - - private ApplicationContext applicationContext; - - @Override - public void setApplicationContext(ApplicationContext applicationContext) - throws BeansException { - this.applicationContext = applicationContext; - } - - @Override - public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) - throws BeansException { - OAuth2ExpressionHandlerInjectionPostProcessor processor = new OAuth2ExpressionHandlerInjectionPostProcessor( - this.applicationContext); - beanFactory.addBeanPostProcessor(processor); - } - - private static class OAuth2ExpressionHandlerInjectionPostProcessor - implements BeanPostProcessor { - - private ApplicationContext applicationContext; - - OAuth2ExpressionHandlerInjectionPostProcessor( - ApplicationContext applicationContext) { - this.applicationContext = applicationContext; - } - - @Override - public Object postProcessBeforeInitialization(Object bean, String beanName) - throws BeansException { - return bean; - } - - @Override - public Object postProcessAfterInitialization(Object bean, String beanName) - throws BeansException { - if (bean instanceof DefaultMethodSecurityExpressionHandler - && !(bean instanceof OAuth2MethodSecurityExpressionHandler)) { - return getExpressionHandler( - (DefaultMethodSecurityExpressionHandler) bean); - } - return bean; - } - - private OAuth2MethodSecurityExpressionHandler getExpressionHandler( - DefaultMethodSecurityExpressionHandler bean) { - OAuth2MethodSecurityExpressionHandler handler = new OAuth2MethodSecurityExpressionHandler(); - handler.setApplicationContext(this.applicationContext); - AuthenticationTrustResolver trustResolver = findInContext( - AuthenticationTrustResolver.class); - if (trustResolver != null) { - handler.setTrustResolver(trustResolver); - } - handler.setExpressionParser(bean.getExpressionParser()); - return handler; - } - - private T findInContext(Class type) { - if (BeanFactoryUtils.beanNamesForTypeIncludingAncestors( - this.applicationContext, type).length == 1) { - return this.applicationContext.getBean(type); - } - return null; - } - - } - -} diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/AuthoritiesExtractor.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/AuthoritiesExtractor.java deleted file mode 100644 index ac6a49a748..0000000000 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/AuthoritiesExtractor.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2012-2017 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.boot.autoconfigure.security.oauth2.resource; - -import java.util.List; -import java.util.Map; - -import org.springframework.security.core.GrantedAuthority; - -/** - * Strategy used by {@link UserInfoTokenServices} to extract authorities from the resource - * server's response. - * - * @author Dave Syer - * @since 1.3.0 - */ -@FunctionalInterface -public interface AuthoritiesExtractor { - - /** - * Extract the authorities from the resource server's response. - * @param map the response - * @return the extracted authorities - */ - List extractAuthorities(Map map); - -} diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/DefaultUserInfoRestTemplateFactory.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/DefaultUserInfoRestTemplateFactory.java deleted file mode 100644 index ac30aeec06..0000000000 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/DefaultUserInfoRestTemplateFactory.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright 2012-2017 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.boot.autoconfigure.security.oauth2.resource; - -import java.util.List; - -import org.springframework.beans.factory.ObjectProvider; -import org.springframework.boot.autoconfigure.security.oauth2.resource.ResourceServerTokenServicesConfiguration.AcceptJsonRequestEnhancer; -import org.springframework.boot.autoconfigure.security.oauth2.resource.ResourceServerTokenServicesConfiguration.AcceptJsonRequestInterceptor; -import org.springframework.core.annotation.AnnotationAwareOrderComparator; -import org.springframework.security.oauth2.client.OAuth2ClientContext; -import org.springframework.security.oauth2.client.OAuth2RestTemplate; -import org.springframework.security.oauth2.client.resource.OAuth2ProtectedResourceDetails; -import org.springframework.security.oauth2.client.token.grant.code.AuthorizationCodeAccessTokenProvider; -import org.springframework.security.oauth2.client.token.grant.code.AuthorizationCodeResourceDetails; -import org.springframework.util.CollectionUtils; - -/** - * Factory used to create the {@link OAuth2RestTemplate} used for extracting user info - * during authentication if none is available. - * - * @author Dave Syer - * @author Stephane Nicoll - * @since 1.5.0 - */ -public class DefaultUserInfoRestTemplateFactory implements UserInfoRestTemplateFactory { - - private static final AuthorizationCodeResourceDetails DEFAULT_RESOURCE_DETAILS; - - static { - AuthorizationCodeResourceDetails details = new AuthorizationCodeResourceDetails(); - details.setClientId(""); - details.setUserAuthorizationUri("Not a URI because there is no client"); - details.setAccessTokenUri("Not a URI because there is no client"); - DEFAULT_RESOURCE_DETAILS = details; - } - - private final List customizers; - - private final OAuth2ProtectedResourceDetails details; - - private final OAuth2ClientContext oauth2ClientContext; - - private OAuth2RestTemplate oauth2RestTemplate; - - public DefaultUserInfoRestTemplateFactory( - ObjectProvider> customizers, - ObjectProvider details, - ObjectProvider oauth2ClientContext) { - this.customizers = customizers.getIfAvailable(); - this.details = details.getIfAvailable(); - this.oauth2ClientContext = oauth2ClientContext.getIfAvailable(); - } - - @Override - public OAuth2RestTemplate getUserInfoRestTemplate() { - if (this.oauth2RestTemplate == null) { - this.oauth2RestTemplate = createOAuth2RestTemplate( - this.details == null ? DEFAULT_RESOURCE_DETAILS : this.details); - this.oauth2RestTemplate.getInterceptors() - .add(new AcceptJsonRequestInterceptor()); - AuthorizationCodeAccessTokenProvider accessTokenProvider = new AuthorizationCodeAccessTokenProvider(); - accessTokenProvider.setTokenRequestEnhancer(new AcceptJsonRequestEnhancer()); - this.oauth2RestTemplate.setAccessTokenProvider(accessTokenProvider); - if (!CollectionUtils.isEmpty(this.customizers)) { - AnnotationAwareOrderComparator.sort(this.customizers); - for (UserInfoRestTemplateCustomizer customizer : this.customizers) { - customizer.customize(this.oauth2RestTemplate); - } - } - } - return this.oauth2RestTemplate; - } - - private OAuth2RestTemplate createOAuth2RestTemplate( - OAuth2ProtectedResourceDetails details) { - if (this.oauth2ClientContext == null) { - return new OAuth2RestTemplate(details); - } - return new OAuth2RestTemplate(details, this.oauth2ClientContext); - } - -} diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/FixedAuthoritiesExtractor.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/FixedAuthoritiesExtractor.java deleted file mode 100644 index fdb3468d93..0000000000 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/FixedAuthoritiesExtractor.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright 2012-2017 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.boot.autoconfigure.security.oauth2.resource; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.Map; - -import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.core.authority.AuthorityUtils; -import org.springframework.util.ObjectUtils; -import org.springframework.util.StringUtils; - -/** - * Default implementation of {@link AuthoritiesExtractor}. Extracts the authorities from - * the map with the key {@code authorities}. If no such value exists, a single - * {@code ROLE_USER} authority is returned. - * - * @author Dave Syer - * @since 1.3.0 - */ -public class FixedAuthoritiesExtractor implements AuthoritiesExtractor { - - private static final String AUTHORITIES = "authorities"; - - private static final String[] AUTHORITY_KEYS = { "authority", "role", "value" }; - - @Override - public List extractAuthorities(Map map) { - String authorities = "ROLE_USER"; - if (map.containsKey(AUTHORITIES)) { - authorities = asAuthorities(map.get(AUTHORITIES)); - } - return AuthorityUtils.commaSeparatedStringToAuthorityList(authorities); - } - - private String asAuthorities(Object object) { - List authorities = new ArrayList<>(); - if (object instanceof Collection) { - Collection collection = (Collection) object; - object = collection.toArray(new Object[0]); - } - if (ObjectUtils.isArray(object)) { - Object[] array = (Object[]) object; - for (Object value : array) { - if (value instanceof String) { - authorities.add(value); - } - else if (value instanceof Map) { - authorities.add(asAuthority((Map) value)); - } - else { - authorities.add(value); - } - } - return StringUtils.collectionToCommaDelimitedString(authorities); - } - return object.toString(); - } - - private Object asAuthority(Map map) { - if (map.size() == 1) { - return map.values().iterator().next(); - } - for (String key : AUTHORITY_KEYS) { - if (map.containsKey(key)) { - return map.get(key); - } - } - return map; - } - -} diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/FixedPrincipalExtractor.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/FixedPrincipalExtractor.java deleted file mode 100644 index ca85d14bf6..0000000000 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/FixedPrincipalExtractor.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2012-2016 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.boot.autoconfigure.security.oauth2.resource; - -import java.util.Map; - -/** - * Default implementation of {@link PrincipalExtractor}. Extracts the principal from the - * map with well known keys. - * - * @author Phillip Webb - * @since 1.4.0 - */ -public class FixedPrincipalExtractor implements PrincipalExtractor { - - private static final String[] PRINCIPAL_KEYS = new String[] { "user", "username", - "userid", "user_id", "login", "id", "name" }; - - @Override - public Object extractPrincipal(Map map) { - for (String key : PRINCIPAL_KEYS) { - if (map.containsKey(key)) { - return map.get(key); - } - } - return null; - } - -} 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 deleted file mode 100644 index 8d6ee2119c..0000000000 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/JwtAccessTokenConverterConfigurer.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2012-2017 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.boot.autoconfigure.security.oauth2.resource; - -import 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 - */ -@FunctionalInterface -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/JwtAccessTokenConverterRestTemplateCustomizer.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/JwtAccessTokenConverterRestTemplateCustomizer.java deleted file mode 100644 index 2fa90587b4..0000000000 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/JwtAccessTokenConverterRestTemplateCustomizer.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2012-2017 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.boot.autoconfigure.security.oauth2.resource; - -import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter; -import org.springframework.web.client.RestTemplate; - -/** - * Callback for customizing the {@link RestTemplate} that is used to fetch the keys used - * by {@link JwtAccessTokenConverter}. - * - * @author Eddú Meléndez - * @since 1.5.2 - * @see JwtAccessTokenConverter#setSigningKey(String) - * @see JwtAccessTokenConverter#setVerifierKey(String) - */ -@FunctionalInterface -public interface JwtAccessTokenConverterRestTemplateCustomizer { - - /** - * Customize the {@code template} before it is initialized. - * @param template the rest template - */ - void customize(RestTemplate template); - -} 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 deleted file mode 100644 index 092bf46f0d..0000000000 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/OAuth2ResourceServerConfiguration.java +++ /dev/null @@ -1,190 +0,0 @@ -/* - * Copyright 2012-2017 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.boot.autoconfigure.security.oauth2.resource; - -import java.util.Map; - -import org.springframework.beans.BeanUtils; -import org.springframework.boot.autoconfigure.condition.ConditionMessage; -import org.springframework.boot.autoconfigure.condition.ConditionOutcome; -import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; -import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; -import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; -import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; -import org.springframework.boot.autoconfigure.condition.SpringBootCondition; -import org.springframework.boot.autoconfigure.security.SecurityProperties; -import org.springframework.boot.autoconfigure.security.oauth2.resource.OAuth2ResourceServerConfiguration.ResourceServerCondition; -import org.springframework.boot.context.properties.bind.Bindable; -import org.springframework.boot.context.properties.bind.Binder; -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.ConfigurableEnvironment; -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; -import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfiguration; -import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurer; -import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter; -import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer; -import org.springframework.util.ClassUtils; -import org.springframework.util.StringUtils; - -/** - * Auto-configure a Spring Security OAuth2 resource server. Back off if another - * {@link ResourceServerConfigurer} already exists or if resource server not enabled. - * - * @author Greg Turnquist - * @author Dave Syer - * @author Madhura Bhave - * @since 1.3.0 - */ -@Configuration -@Conditional(ResourceServerCondition.class) -@ConditionalOnClass({ EnableResourceServer.class, SecurityProperties.class }) -@ConditionalOnWebApplication -@ConditionalOnBean(ResourceServerConfiguration.class) -@Import(ResourceServerTokenServicesConfiguration.class) -public class OAuth2ResourceServerConfiguration { - - private final ResourceServerProperties resource; - - public OAuth2ResourceServerConfiguration(ResourceServerProperties resource) { - this.resource = resource; - } - - @Bean - @ConditionalOnMissingBean(ResourceServerConfigurer.class) - public ResourceServerConfigurer resourceServer() { - return new ResourceSecurityConfigurer(this.resource); - } - - protected static class ResourceSecurityConfigurer - extends ResourceServerConfigurerAdapter { - - private ResourceServerProperties resource; - - public ResourceSecurityConfigurer(ResourceServerProperties resource) { - this.resource = resource; - } - - @Override - public void configure(ResourceServerSecurityConfigurer resources) - throws Exception { - resources.resourceId(this.resource.getResourceId()); - } - - @Override - public void configure(HttpSecurity http) throws Exception { - http.authorizeRequests().anyRequest().authenticated(); - } - - } - - protected static class ResourceServerCondition extends SpringBootCondition - implements ConfigurationCondition { - - private static final Bindable> STRING_OBJECT_MAP = Bindable - .mapOf(String.class, Object.class); - - private static final String AUTHORIZATION_ANNOTATION = "org.springframework." - + "security.oauth2.config.annotation.web.configuration." - + "AuthorizationServerEndpointsConfiguration"; - - @Override - public ConfigurationPhase getConfigurationPhase() { - return ConfigurationPhase.REGISTER_BEAN; - } - - @Override - public ConditionOutcome getMatchOutcome(ConditionContext context, - AnnotatedTypeMetadata metadata) { - ConditionMessage.Builder message = ConditionMessage - .forCondition("OAuth ResourceServer Condition"); - Environment environment = context.getEnvironment(); - if (!(environment instanceof ConfigurableEnvironment)) { - return ConditionOutcome - .noMatch(message.didNotFind("A ConfigurableEnvironment").atAll()); - } - if (hasOAuthClientId(environment)) { - return ConditionOutcome.match(message.foundExactly("client-id property")); - } - Binder binder = Binder.get(environment); - String prefix = "security.oauth2.resource."; - if (binder.bind(prefix + "jwt", STRING_OBJECT_MAP).isBound()) { - return ConditionOutcome - .match(message.foundExactly("JWT resource configuration")); - } - if (binder.bind(prefix + "jwk", STRING_OBJECT_MAP).isBound()) { - return ConditionOutcome - .match(message.foundExactly("JWK resource configuration")); - } - if (StringUtils.hasText(environment.getProperty(prefix + "user-info-uri"))) { - return ConditionOutcome - .match(message.foundExactly("user-info-uri property")); - } - if (StringUtils.hasText(environment.getProperty(prefix + "token-info-uri"))) { - return ConditionOutcome - .match(message.foundExactly("token-info-uri property")); - } - if (ClassUtils.isPresent(AUTHORIZATION_ANNOTATION, null)) { - if (AuthorizationServerEndpointsConfigurationBeanCondition - .matches(context)) { - return ConditionOutcome.match( - message.found("class").items(AUTHORIZATION_ANNOTATION)); - } - } - return ConditionOutcome.noMatch( - message.didNotFind("client ID, JWT resource or authorization server") - .atAll()); - } - - private boolean hasOAuthClientId(Environment environment) { - return StringUtils.hasLength( - environment.getProperty("security.oauth2.client.client-id")); - } - - } - - @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/PrincipalExtractor.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/PrincipalExtractor.java deleted file mode 100644 index 9bcd39d015..0000000000 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/PrincipalExtractor.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2012-2017 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.boot.autoconfigure.security.oauth2.resource; - -import java.util.Map; - -/** - * Strategy used by {@link UserInfoTokenServices} to extract the principal from the - * resource server's response. - * - * @author Phillip Webb - * @since 1.4.0 - */ -@FunctionalInterface -public interface PrincipalExtractor { - - /** - * Extract the principal that should be used for the token. - * @param map the source map - * @return the extracted principal or {@code null} - */ - Object extractPrincipal(Map map); - -} 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 deleted file mode 100644 index e589168f9f..0000000000 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/ResourceServerProperties.java +++ /dev/null @@ -1,284 +0,0 @@ -/* - * Copyright 2012-2017 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.boot.autoconfigure.security.oauth2.resource; - -import javax.annotation.PostConstruct; - -import com.fasterxml.jackson.annotation.JsonIgnore; - -import org.springframework.beans.BeansException; -import org.springframework.beans.factory.BeanFactory; -import org.springframework.beans.factory.BeanFactoryAware; -import org.springframework.beans.factory.BeanFactoryUtils; -import org.springframework.beans.factory.ListableBeanFactory; -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken; -import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerEndpointsConfiguration; -import org.springframework.util.StringUtils; -import org.springframework.validation.BeanPropertyBindingResult; -import org.springframework.validation.BindException; -import org.springframework.validation.BindingResult; - -/** - * Configuration properties for OAuth2 Resources. - * - * @author Dave Syer - * @author Madhura Bhave - * @since 1.3.0 - */ -@ConfigurationProperties(prefix = "security.oauth2.resource") -public class ResourceServerProperties implements BeanFactoryAware { - - @JsonIgnore - private final String clientId; - - @JsonIgnore - private final String clientSecret; - - @JsonIgnore - private ListableBeanFactory beanFactory; - - private String serviceId = "resource"; - - /** - * Identifier of the resource. - */ - private String id; - - /** - * URI of the user endpoint. - */ - private String userInfoUri; - - /** - * URI of the token decoding endpoint. - */ - private String tokenInfoUri; - - /** - * Use the token info, can be set to false to use the user info. - */ - private boolean preferTokenInfo = true; - - /** - * The token type to send when using the userInfoUri. - */ - private String tokenType = DefaultOAuth2AccessToken.BEARER_TYPE; - - private Jwt jwt = new Jwt(); - - private Jwk jwk = new Jwk(); - - public ResourceServerProperties() { - this(null, null); - } - - public ResourceServerProperties(String clientId, String clientSecret) { - this.clientId = clientId; - this.clientSecret = clientSecret; - } - - @Override - public void setBeanFactory(BeanFactory beanFactory) throws BeansException { - this.beanFactory = (ListableBeanFactory) beanFactory; - } - - public String getResourceId() { - return this.id; - } - - public String getServiceId() { - return this.serviceId; - } - - public void setServiceId(String serviceId) { - this.serviceId = serviceId; - } - - public String getId() { - return this.id; - } - - public void setId(String id) { - this.id = id; - } - - public String getUserInfoUri() { - return this.userInfoUri; - } - - public void setUserInfoUri(String userInfoUri) { - this.userInfoUri = userInfoUri; - } - - public String getTokenInfoUri() { - return this.tokenInfoUri; - } - - public void setTokenInfoUri(String tokenInfoUri) { - this.tokenInfoUri = tokenInfoUri; - } - - public boolean isPreferTokenInfo() { - return this.preferTokenInfo; - } - - public void setPreferTokenInfo(boolean preferTokenInfo) { - this.preferTokenInfo = preferTokenInfo; - } - - public String getTokenType() { - return this.tokenType; - } - - public void setTokenType(String tokenType) { - this.tokenType = tokenType; - } - - public Jwt getJwt() { - return this.jwt; - } - - public void setJwt(Jwt jwt) { - this.jwt = jwt; - } - - public Jwk getJwk() { - return this.jwk; - } - - public void setJwk(Jwk jwk) { - this.jwk = jwk; - } - - public String getClientId() { - return this.clientId; - } - - public String getClientSecret() { - return this.clientSecret; - } - - @PostConstruct - public void validate() { - if (countBeans(AuthorizationServerEndpointsConfiguration.class) > 0) { - // If we are an authorization server we don't need remote resource token - // services - return; - } - if (countBeans(ResourceServerTokenServicesConfiguration.class) == 0) { - // If we are not a resource server or an SSO client we don't need remote - // resource token services - return; - } - if (!StringUtils.hasText(this.clientId)) { - return; - } - try { - doValidate(); - } - catch (BindException ex) { - throw new IllegalStateException(ex); - } - } - - private int countBeans(Class type) { - return BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this.beanFactory, type, - true, false).length; - } - - private void doValidate() throws BindException { - BindingResult errors = new BeanPropertyBindingResult(this, - "resourceServerProperties"); - boolean jwtConfigPresent = StringUtils.hasText(this.jwt.getKeyUri()) - || StringUtils.hasText(this.jwt.getKeyValue()); - boolean jwkConfigPresent = StringUtils.hasText(this.jwk.getKeySetUri()); - if (jwtConfigPresent && jwkConfigPresent) { - errors.reject("ambiguous.keyUri", - "Only one of jwt.keyUri (or jwt.keyValue) and jwk.keySetUri should" - + " be configured."); - } - if (!jwtConfigPresent && !jwkConfigPresent) { - if (!StringUtils.hasText(this.userInfoUri) - && !StringUtils.hasText(this.tokenInfoUri)) { - errors.rejectValue("tokenInfoUri", "missing.tokenInfoUri", - "Missing tokenInfoUri and userInfoUri and there is no " - + "JWT verifier key"); - } - if (StringUtils.hasText(this.tokenInfoUri) && isPreferTokenInfo()) { - if (!StringUtils.hasText(this.clientSecret)) { - errors.rejectValue("clientSecret", "missing.clientSecret", - "Missing client secret"); - } - } - } - if (errors.hasErrors()) { - throw new BindException(errors); - } - } - - public class Jwt { - - /** - * The verification key of the JWT token. Can either be a symmetric secret or - * PEM-encoded RSA public key. If the value is not available, you can set the URI - * instead. - */ - private String keyValue; - - /** - * The URI of the JWT token. Can be set if the value is not available and the key - * is public. - */ - private String keyUri; - - public String getKeyValue() { - return this.keyValue; - } - - public void setKeyValue(String keyValue) { - this.keyValue = keyValue; - } - - public void setKeyUri(String keyUri) { - this.keyUri = keyUri; - } - - public String getKeyUri() { - return this.keyUri; - } - - } - - public class Jwk { - - /** - * The URI to get verification keys to verify the JWT token. This can be set when - * the authorization server returns a set of verification keys. - */ - private String keySetUri; - - public String getKeySetUri() { - return this.keySetUri; - } - - public void setKeySetUri(String keySetUri) { - this.keySetUri = keySetUri; - } - } - -} 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 deleted file mode 100644 index 8ea87ce5e7..0000000000 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/ResourceServerTokenServicesConfiguration.java +++ /dev/null @@ -1,443 +0,0 @@ -/* - * Copyright 2012-2017 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.boot.autoconfigure.security.oauth2.resource; - -import java.io.IOException; -import java.util.Arrays; -import java.util.Base64; -import java.util.List; -import java.util.Map; - -import org.springframework.beans.factory.ObjectProvider; -import org.springframework.boot.autoconfigure.condition.ConditionMessage; -import org.springframework.boot.autoconfigure.condition.ConditionOutcome; -import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; -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.NoneNestedConditions; -import org.springframework.boot.autoconfigure.condition.SpringBootCondition; -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.annotation.AnnotationAwareOrderComparator; -import org.springframework.core.env.Environment; -import org.springframework.core.type.AnnotatedTypeMetadata; -import org.springframework.http.HttpEntity; -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpMethod; -import org.springframework.http.HttpRequest; -import org.springframework.http.MediaType; -import org.springframework.http.client.ClientHttpRequestExecution; -import org.springframework.http.client.ClientHttpRequestInterceptor; -import org.springframework.http.client.ClientHttpResponse; -import org.springframework.security.oauth2.client.OAuth2ClientContext; -import org.springframework.security.oauth2.client.OAuth2RestOperations; -import org.springframework.security.oauth2.client.resource.OAuth2ProtectedResourceDetails; -import org.springframework.security.oauth2.client.token.AccessTokenRequest; -import org.springframework.security.oauth2.client.token.RequestEnhancer; -import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerEndpointsConfiguration; -import org.springframework.security.oauth2.provider.token.DefaultTokenServices; -import org.springframework.security.oauth2.provider.token.RemoteTokenServices; -import org.springframework.security.oauth2.provider.token.ResourceServerTokenServices; -import org.springframework.security.oauth2.provider.token.TokenStore; -import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter; -import org.springframework.security.oauth2.provider.token.store.JwtTokenStore; -import org.springframework.security.oauth2.provider.token.store.jwk.JwkTokenStore; -import org.springframework.social.connect.ConnectionFactoryLocator; -import org.springframework.social.connect.support.OAuth2ConnectionFactory; -import org.springframework.util.CollectionUtils; -import org.springframework.util.MultiValueMap; -import org.springframework.util.StringUtils; -import org.springframework.web.client.RestTemplate; - -/** - * Configuration for an OAuth2 resource server. - * - * @author Dave Syer - * @author Madhura Bhave - * @author Eddú Meléndez - * @since 1.3.0 - */ -@Configuration -@ConditionalOnMissingBean(AuthorizationServerEndpointsConfiguration.class) -public class ResourceServerTokenServicesConfiguration { - - @Bean - @ConditionalOnMissingBean - public UserInfoRestTemplateFactory userInfoRestTemplateFactory( - ObjectProvider> customizers, - ObjectProvider details, - ObjectProvider oauth2ClientContext) { - return new DefaultUserInfoRestTemplateFactory(customizers, details, - oauth2ClientContext); - } - - @Configuration - @Conditional(RemoteTokenCondition.class) - protected static class RemoteTokenServicesConfiguration { - - @Configuration - @Conditional(TokenInfoCondition.class) - protected static class TokenInfoServicesConfiguration { - - private final ResourceServerProperties resource; - - protected TokenInfoServicesConfiguration(ResourceServerProperties resource) { - this.resource = resource; - } - - @Bean - public RemoteTokenServices remoteTokenServices() { - RemoteTokenServices services = new RemoteTokenServices(); - services.setCheckTokenEndpointUrl(this.resource.getTokenInfoUri()); - services.setClientId(this.resource.getClientId()); - services.setClientSecret(this.resource.getClientSecret()); - return services; - } - - } - - @Configuration - @ConditionalOnClass(OAuth2ConnectionFactory.class) - @Conditional(NotTokenInfoCondition.class) - protected static class SocialTokenServicesConfiguration { - - private final ResourceServerProperties sso; - - private final OAuth2ConnectionFactory connectionFactory; - - private final OAuth2RestOperations restTemplate; - - private final AuthoritiesExtractor authoritiesExtractor; - - private final PrincipalExtractor principalExtractor; - - public SocialTokenServicesConfiguration(ResourceServerProperties sso, - ObjectProvider> connectionFactory, - UserInfoRestTemplateFactory restTemplateFactory, - ObjectProvider authoritiesExtractor, - ObjectProvider principalExtractor) { - this.sso = sso; - this.connectionFactory = connectionFactory.getIfAvailable(); - this.restTemplate = restTemplateFactory.getUserInfoRestTemplate(); - this.authoritiesExtractor = authoritiesExtractor.getIfAvailable(); - this.principalExtractor = principalExtractor.getIfAvailable(); - } - - @Bean - @ConditionalOnBean(ConnectionFactoryLocator.class) - @ConditionalOnMissingBean(ResourceServerTokenServices.class) - public SpringSocialTokenServices socialTokenServices() { - return new SpringSocialTokenServices(this.connectionFactory, - this.sso.getClientId()); - } - - @Bean - @ConditionalOnMissingBean({ ConnectionFactoryLocator.class, - ResourceServerTokenServices.class }) - public UserInfoTokenServices userInfoTokenServices() { - UserInfoTokenServices services = new UserInfoTokenServices( - this.sso.getUserInfoUri(), this.sso.getClientId()); - services.setTokenType(this.sso.getTokenType()); - services.setRestTemplate(this.restTemplate); - if (this.authoritiesExtractor != null) { - services.setAuthoritiesExtractor(this.authoritiesExtractor); - } - if (this.principalExtractor != null) { - services.setPrincipalExtractor(this.principalExtractor); - } - return services; - } - - } - - @Configuration - @ConditionalOnMissingClass("org.springframework.social.connect.support.OAuth2ConnectionFactory") - @Conditional(NotTokenInfoCondition.class) - protected static class UserInfoTokenServicesConfiguration { - - private final ResourceServerProperties sso; - - private final OAuth2RestOperations restTemplate; - - private final AuthoritiesExtractor authoritiesExtractor; - - private final PrincipalExtractor principalExtractor; - - public UserInfoTokenServicesConfiguration(ResourceServerProperties sso, - UserInfoRestTemplateFactory restTemplateFactory, - ObjectProvider authoritiesExtractor, - ObjectProvider principalExtractor) { - this.sso = sso; - this.restTemplate = restTemplateFactory.getUserInfoRestTemplate(); - this.authoritiesExtractor = authoritiesExtractor.getIfAvailable(); - this.principalExtractor = principalExtractor.getIfAvailable(); - } - - @Bean - @ConditionalOnMissingBean(ResourceServerTokenServices.class) - public UserInfoTokenServices userInfoTokenServices() { - UserInfoTokenServices services = new UserInfoTokenServices( - this.sso.getUserInfoUri(), this.sso.getClientId()); - services.setRestTemplate(this.restTemplate); - services.setTokenType(this.sso.getTokenType()); - if (this.authoritiesExtractor != null) { - services.setAuthoritiesExtractor(this.authoritiesExtractor); - } - if (this.principalExtractor != null) { - services.setPrincipalExtractor(this.principalExtractor); - } - return services; - } - - } - - } - - @Configuration - @Conditional(JwkCondition.class) - protected static class JwkTokenStoreConfiguration { - - private final ResourceServerProperties resource; - - public JwkTokenStoreConfiguration(ResourceServerProperties resource) { - this.resource = resource; - } - - @Bean - @ConditionalOnMissingBean(ResourceServerTokenServices.class) - public DefaultTokenServices jwkTokenServices(TokenStore jwkTokenStore) { - DefaultTokenServices services = new DefaultTokenServices(); - services.setTokenStore(jwkTokenStore); - return services; - } - - @Bean - @ConditionalOnMissingBean(TokenStore.class) - public TokenStore jwkTokenStore() { - return new JwkTokenStore(this.resource.getJwk().getKeySetUri()); - } - } - - @Configuration - @Conditional(JwtTokenCondition.class) - protected static class JwtTokenServicesConfiguration { - - private final ResourceServerProperties resource; - - private final List configurers; - - private final List customizers; - - public JwtTokenServicesConfiguration(ResourceServerProperties resource, - ObjectProvider> configurers, - ObjectProvider> customizers) { - this.resource = resource; - this.configurers = configurers.getIfAvailable(); - this.customizers = customizers.getIfAvailable(); - } - - @Bean - @ConditionalOnMissingBean(ResourceServerTokenServices.class) - public DefaultTokenServices jwtTokenServices(TokenStore jwtTokenStore) { - DefaultTokenServices services = new DefaultTokenServices(); - services.setTokenStore(jwtTokenStore); - return services; - } - - @Bean - @ConditionalOnMissingBean(TokenStore.class) - public TokenStore jwtTokenStore() { - return new JwtTokenStore(jwtTokenEnhancer()); - } - - @Bean - public JwtAccessTokenConverter jwtTokenEnhancer() { - JwtAccessTokenConverter converter = new JwtAccessTokenConverter(); - String keyValue = this.resource.getJwt().getKeyValue(); - if (!StringUtils.hasText(keyValue)) { - keyValue = getKeyFromServer(); - } - if (StringUtils.hasText(keyValue) && !keyValue.startsWith("-----BEGIN")) { - converter.setSigningKey(keyValue); - } - if (keyValue != null) { - converter.setVerifierKey(keyValue); - } - if (!CollectionUtils.isEmpty(this.configurers)) { - AnnotationAwareOrderComparator.sort(this.configurers); - for (JwtAccessTokenConverterConfigurer configurer : this.configurers) { - configurer.configure(converter); - } - } - return converter; - } - - private String getKeyFromServer() { - RestTemplate keyUriRestTemplate = new RestTemplate(); - if (!CollectionUtils.isEmpty(this.customizers)) { - for (JwtAccessTokenConverterRestTemplateCustomizer customizer : this.customizers) { - customizer.customize(keyUriRestTemplate); - } - } - HttpHeaders headers = new HttpHeaders(); - String username = this.resource.getClientId(); - String password = this.resource.getClientSecret(); - if (username != null && password != null) { - byte[] token = Base64.getEncoder() - .encode((username + ":" + password).getBytes()); - headers.add("Authorization", "Basic " + new String(token)); - } - HttpEntity request = new HttpEntity<>(headers); - String url = this.resource.getJwt().getKeyUri(); - return (String) keyUriRestTemplate - .exchange(url, HttpMethod.GET, request, Map.class).getBody() - .get("value"); - } - - } - - private static class TokenInfoCondition extends SpringBootCondition { - - @Override - public ConditionOutcome getMatchOutcome(ConditionContext context, - AnnotatedTypeMetadata metadata) { - ConditionMessage.Builder message = ConditionMessage - .forCondition("OAuth TokenInfo Condition"); - Environment environment = context.getEnvironment(); - Boolean preferTokenInfo = environment.getProperty( - "security.oauth2.resource.prefer-token-info", Boolean.class); - if (preferTokenInfo == null) { - preferTokenInfo = environment - .resolvePlaceholders("${OAUTH2_RESOURCE_PREFERTOKENINFO:true}") - .equals("true"); - } - String tokenInfoUri = environment - .getProperty("security.oauth2.resource.token-info-uri"); - String userInfoUri = environment - .getProperty("security.oauth2.resource.user-info-uri"); - if (!StringUtils.hasLength(userInfoUri) - && !StringUtils.hasLength(tokenInfoUri)) { - return ConditionOutcome - .match(message.didNotFind("user-info-uri property").atAll()); - } - if (StringUtils.hasLength(tokenInfoUri) && preferTokenInfo) { - return ConditionOutcome - .match(message.foundExactly("preferred token-info-uri property")); - } - return ConditionOutcome.noMatch(message.didNotFind("token info").atAll()); - } - - } - - private static class JwtTokenCondition extends SpringBootCondition { - - @Override - public ConditionOutcome getMatchOutcome(ConditionContext context, - AnnotatedTypeMetadata metadata) { - ConditionMessage.Builder message = ConditionMessage - .forCondition("OAuth JWT Condition"); - Environment environment = context.getEnvironment(); - String keyValue = environment - .getProperty("security.oauth2.resource.jwt.key-value"); - String keyUri = environment - .getProperty("security.oauth2.resource.jwt.key-uri"); - if (StringUtils.hasText(keyValue) || StringUtils.hasText(keyUri)) { - return ConditionOutcome - .match(message.foundExactly("provided public key")); - } - return ConditionOutcome - .noMatch(message.didNotFind("provided public key").atAll()); - } - - } - - private static class JwkCondition extends SpringBootCondition { - - @Override - public ConditionOutcome getMatchOutcome(ConditionContext context, - AnnotatedTypeMetadata metadata) { - ConditionMessage.Builder message = ConditionMessage - .forCondition("OAuth JWK Condition"); - Environment environment = context.getEnvironment(); - String keyUri = environment - .getProperty("security.oauth2.resource.jwk.key-set-uri"); - if (StringUtils.hasText(keyUri)) { - return ConditionOutcome - .match(message.foundExactly("provided jwk key set URI")); - } - return ConditionOutcome - .noMatch(message.didNotFind("key jwk set URI not provided").atAll()); - } - - } - - private static class NotTokenInfoCondition extends SpringBootCondition { - - private TokenInfoCondition tokenInfoCondition = new TokenInfoCondition(); - - @Override - public ConditionOutcome getMatchOutcome(ConditionContext context, - AnnotatedTypeMetadata metadata) { - return ConditionOutcome - .inverse(this.tokenInfoCondition.getMatchOutcome(context, metadata)); - } - - } - - private static class RemoteTokenCondition extends NoneNestedConditions { - - RemoteTokenCondition() { - super(ConfigurationPhase.PARSE_CONFIGURATION); - } - - @Conditional(JwtTokenCondition.class) - static class HasJwtConfiguration { - - } - - @Conditional(JwkCondition.class) - static class HasJwkConfiguration { - - } - } - - 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); - } - - } - - 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 deleted file mode 100644 index 2589c6a872..0000000000 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/SpringSocialTokenServices.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright 2012-2016 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.boot.autoconfigure.security.oauth2.resource; - -import java.util.List; - -import org.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; -import org.springframework.security.oauth2.provider.OAuth2Authentication; -import org.springframework.security.oauth2.provider.OAuth2Request; -import org.springframework.security.oauth2.provider.token.ResourceServerTokenServices; -import org.springframework.social.connect.Connection; -import org.springframework.social.connect.UserProfile; -import org.springframework.social.connect.support.OAuth2ConnectionFactory; -import org.springframework.social.oauth2.AccessGrant; - -/** - * {@link ResourceServerTokenServices} backed by Spring Social. - * - * @author Dave Syer - * @since 1.3.0 - */ -public class SpringSocialTokenServices implements ResourceServerTokenServices { - - private final OAuth2ConnectionFactory connectionFactory; - - private final String clientId; - - public SpringSocialTokenServices(OAuth2ConnectionFactory connectionFactory, - String clientId) { - this.connectionFactory = connectionFactory; - this.clientId = clientId; - } - - @Override - public OAuth2Authentication loadAuthentication(String accessToken) - throws AuthenticationException, InvalidTokenException { - AccessGrant accessGrant = new AccessGrant(accessToken); - Connection connection = this.connectionFactory.createConnection(accessGrant); - UserProfile user = connection.fetchUserProfile(); - return extractAuthentication(user); - } - - private OAuth2Authentication extractAuthentication(UserProfile user) { - 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 - public OAuth2AccessToken readAccessToken(String accessToken) { - throw new UnsupportedOperationException("Not supported: read access token"); - } - -} 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 deleted file mode 100644 index 4964a546e4..0000000000 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/UserInfoRestTemplateCustomizer.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2012-2017 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.boot.autoconfigure.security.oauth2.resource; - -import org.springframework.security.oauth2.client.OAuth2RestTemplate; - -/** - * Callback for customizing the rest template used to fetch user details if authentication - * is done via OAuth2 access tokens. The default should be fine for most providers, but - * occasionally you might need to add additional interceptors, or change the request - * 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 - * @since 1.3.0 - */ -@FunctionalInterface -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/UserInfoRestTemplateFactory.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/UserInfoRestTemplateFactory.java deleted file mode 100644 index 5360e56142..0000000000 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/UserInfoRestTemplateFactory.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2012-2017 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.boot.autoconfigure.security.oauth2.resource; - -import org.springframework.security.oauth2.client.OAuth2RestTemplate; - -/** - * Factory used to create the {@link OAuth2RestTemplate} used for extracting user info - * during authentication if none is available. - * - * @author Dave Syer - * @author Stephane Nicoll - * @since 1.4.0 - */ -@FunctionalInterface -public interface UserInfoRestTemplateFactory { - - /** - * Return the {@link OAuth2RestTemplate} used for extracting user info during - * authentication if none is available. - * @return the OAuth2RestTemplate used for authentication - */ - OAuth2RestTemplate getUserInfoRestTemplate(); - -} 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 deleted file mode 100644 index a3c3445353..0000000000 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/UserInfoTokenServices.java +++ /dev/null @@ -1,156 +0,0 @@ -/* - * Copyright 2012-2017 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.boot.autoconfigure.security.oauth2.resource; - -import java.util.Collections; -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.oauth2.client.OAuth2RestOperations; -import org.springframework.security.oauth2.client.OAuth2RestTemplate; -import org.springframework.security.oauth2.client.resource.BaseOAuth2ProtectedResourceDetails; -import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken; -import org.springframework.security.oauth2.common.OAuth2AccessToken; -import org.springframework.security.oauth2.common.exceptions.InvalidTokenException; -import org.springframework.security.oauth2.provider.OAuth2Authentication; -import org.springframework.security.oauth2.provider.OAuth2Request; -import org.springframework.security.oauth2.provider.token.ResourceServerTokenServices; -import org.springframework.util.Assert; - -/** - * {@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 final String userInfoEndpointUrl; - - private final String clientId; - - private OAuth2RestOperations restTemplate; - - private String tokenType = DefaultOAuth2AccessToken.BEARER_TYPE; - - private AuthoritiesExtractor authoritiesExtractor = new FixedAuthoritiesExtractor(); - - private PrincipalExtractor principalExtractor = new FixedPrincipalExtractor(); - - public UserInfoTokenServices(String userInfoEndpointUrl, String clientId) { - this.userInfoEndpointUrl = userInfoEndpointUrl; - this.clientId = clientId; - } - - public void setTokenType(String tokenType) { - this.tokenType = tokenType; - } - - public void setRestTemplate(OAuth2RestOperations restTemplate) { - this.restTemplate = restTemplate; - } - - public void setAuthoritiesExtractor(AuthoritiesExtractor authoritiesExtractor) { - Assert.notNull(authoritiesExtractor, "AuthoritiesExtractor must not be null"); - this.authoritiesExtractor = authoritiesExtractor; - } - - public void setPrincipalExtractor(PrincipalExtractor principalExtractor) { - Assert.notNull(principalExtractor, "PrincipalExtractor must not be null"); - this.principalExtractor = principalExtractor; - } - - @Override - public OAuth2Authentication loadAuthentication(String accessToken) - throws AuthenticationException, InvalidTokenException { - Map map = getMap(this.userInfoEndpointUrl, accessToken); - if (map.containsKey("error")) { - if (this.logger.isDebugEnabled()) { - this.logger.debug("userinfo returned error: " + map.get("error")); - } - throw new InvalidTokenException(accessToken); - } - return extractAuthentication(map); - } - - private OAuth2Authentication extractAuthentication(Map map) { - Object principal = getPrincipal(map); - List authorities = this.authoritiesExtractor - .extractAuthorities(map); - 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); - } - - /** - * Return the principal that should be used for the token. The default implementation - * delegates to the {@link PrincipalExtractor}. - * @param map the source map - * @return the principal or {@literal "unknown"} - */ - protected Object getPrincipal(Map map) { - Object principal = this.principalExtractor.extractPrincipal(map); - return (principal == null ? "unknown" : principal); - } - - @Override - public OAuth2AccessToken readAccessToken(String accessToken) { - throw new UnsupportedOperationException("Not supported: read access token"); - } - - @SuppressWarnings({ "unchecked" }) - private Map getMap(String path, String accessToken) { - if (this.logger.isDebugEnabled()) { - this.logger.debug("Getting user info from: " + path); - } - try { - OAuth2RestOperations restTemplate = this.restTemplate; - if (restTemplate == null) { - BaseOAuth2ProtectedResourceDetails resource = new BaseOAuth2ProtectedResourceDetails(); - resource.setClientId(this.clientId); - restTemplate = new OAuth2RestTemplate(resource); - } - OAuth2AccessToken existingToken = restTemplate.getOAuth2ClientContext() - .getAccessToken(); - if (existingToken == null || !accessToken.equals(existingToken.getValue())) { - DefaultOAuth2AccessToken token = new DefaultOAuth2AccessToken( - accessToken); - token.setTokenType(this.tokenType); - restTemplate.getOAuth2ClientContext().setAccessToken(token); - } - return restTemplate.getForEntity(path, Map.class).getBody(); - } - catch (Exception ex) { - this.logger.warn("Could not fetch user details: " + ex.getClass() + ", " - + ex.getMessage()); - return Collections.singletonMap("error", - "Could not fetch user details"); - } - } - -} diff --git a/spring-boot-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/spring-boot-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json index 7a0ee384ea..79de697bf9 100644 --- a/spring-boot-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json +++ b/spring-boot-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -895,25 +895,6 @@ "level": "error" } }, - { - "name": "security.oauth2.resource.filter-order", - "type": "java.lang.Integer", - "description": "The order of the filter chain used to authenticate tokens. Default puts it after\n the actuator endpoints and before the default HTTP basic filter chain (catchall).", - "defaultValue": 0, - "deprecation": { - "reason": "The security auto-configuration does no longer provide several security configurations. Their ordering is now explicit in your own security configuration.", - "level": "error" - } - }, - { - "name": "security.oauth2.sso.filter-order", - "type": "java.lang.Integer", - "description": "Filter order to apply if not providing an explicit WebSecurityConfigurerAdapter (in\n which case the order can be provided there instead).", - "deprecation": { - "reason": "The security auto-configuration does no longer provide several security configurations. Their ordering is now explicit in your own security configuration.", - "level": "error" - } - }, { "name": "security.require-ssl", "type": "java.lang.Boolean", diff --git a/spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories b/spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories index 9324b916e3..67da10e465 100644 --- a/spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories +++ b/spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories @@ -98,7 +98,6 @@ org.springframework.boot.autoconfigure.quartz.QuartzAutoConfiguration,\ org.springframework.boot.autoconfigure.reactor.core.ReactorCoreAutoConfiguration,\ org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration,\ org.springframework.boot.autoconfigure.security.SecurityFilterAutoConfiguration,\ -org.springframework.boot.autoconfigure.security.oauth2.OAuth2AutoConfiguration,\ org.springframework.boot.autoconfigure.sendgrid.SendGridAutoConfiguration,\ org.springframework.boot.autoconfigure.session.SessionAutoConfiguration,\ org.springframework.boot.autoconfigure.social.SocialWebAutoConfiguration,\ diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/OAuth2AutoConfigurationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/OAuth2AutoConfigurationTests.java deleted file mode 100644 index 99cb5ace2c..0000000000 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/OAuth2AutoConfigurationTests.java +++ /dev/null @@ -1,683 +0,0 @@ -/* - * Copyright 2012-2017 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.boot.autoconfigure.security.oauth2; - -import java.net.URI; -import java.util.Arrays; -import java.util.Base64; -import java.util.List; - -import com.fasterxml.jackson.databind.JsonNode; -import org.junit.Test; - -import org.springframework.aop.support.AopUtils; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration; -import org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration; -import org.springframework.boot.autoconfigure.security.oauth2.authserver.OAuth2AuthorizationServerConfiguration; -import org.springframework.boot.autoconfigure.security.oauth2.method.OAuth2MethodSecurityConfiguration; -import org.springframework.boot.autoconfigure.security.oauth2.resource.OAuth2ResourceServerConfiguration; -import org.springframework.boot.autoconfigure.security.oauth2.resource.ResourceServerProperties; -import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration; -import org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration; -import org.springframework.boot.context.properties.source.ConfigurationPropertySources; -import org.springframework.boot.test.util.TestPropertyValues; -import org.springframework.boot.test.web.client.TestRestTemplate; -import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory; -import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext; -import org.springframework.context.ApplicationContext; -import org.springframework.context.annotation.AnnotationConfigApplicationContext; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Import; -import org.springframework.http.HttpEntity; -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpMethod; -import org.springframework.http.HttpStatus; -import org.springframework.http.RequestEntity; -import org.springframework.http.ResponseEntity; -import org.springframework.security.access.PermissionEvaluator; -import org.springframework.security.access.annotation.Jsr250MethodSecurityMetadataSource; -import org.springframework.security.access.annotation.SecuredAnnotationSecurityMetadataSource; -import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler; -import org.springframework.security.access.hierarchicalroles.RoleHierarchy; -import org.springframework.security.access.method.DelegatingMethodSecurityMetadataSource; -import org.springframework.security.access.method.MethodSecurityMetadataSource; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.security.access.prepost.PreInvocationAuthorizationAdvice; -import org.springframework.security.access.prepost.PrePostAnnotationSecurityMetadataSource; -import org.springframework.security.authentication.AuthenticationManager; -import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; -import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; -import org.springframework.security.config.annotation.method.configuration.GlobalMethodSecurityConfiguration; -import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; -import org.springframework.security.core.authority.AuthorityUtils; -import org.springframework.security.oauth2.client.OAuth2ClientContext; -import org.springframework.security.oauth2.client.OAuth2RestOperations; -import org.springframework.security.oauth2.client.token.grant.client.ClientCredentialsResourceDetails; -import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer; -import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter; -import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer; -import org.springframework.security.oauth2.config.annotation.web.configuration.EnableOAuth2Client; -import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer; -import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter; -import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer; -import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer; -import org.springframework.security.oauth2.provider.ClientDetails; -import org.springframework.security.oauth2.provider.ClientDetailsService; -import org.springframework.security.oauth2.provider.approval.ApprovalStore; -import org.springframework.security.oauth2.provider.approval.ApprovalStoreUserApprovalHandler; -import org.springframework.security.oauth2.provider.approval.TokenApprovalStore; -import org.springframework.security.oauth2.provider.approval.UserApprovalHandler; -import org.springframework.security.oauth2.provider.client.BaseClientDetails; -import org.springframework.security.oauth2.provider.client.InMemoryClientDetailsService; -import org.springframework.security.oauth2.provider.endpoint.AuthorizationEndpoint; -import org.springframework.security.oauth2.provider.expression.OAuth2MethodSecurityExpressionHandler; -import org.springframework.security.oauth2.provider.token.DefaultTokenServices; -import org.springframework.security.oauth2.provider.token.TokenStore; -import org.springframework.security.oauth2.provider.token.store.InMemoryTokenStore; -import org.springframework.test.util.ReflectionTestUtils; -import org.springframework.util.LinkedMultiValueMap; -import org.springframework.util.MultiValueMap; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RestController; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.mock; - -/** - * Verify Spring Security OAuth2 auto-configuration secures end points properly, accepts - * environmental overrides, and also backs off in the presence of other - * resource/authorization components. - * - * @author Greg Turnquist - * @author Dave Syer - */ -public class OAuth2AutoConfigurationTests { - - private static final Class RESOURCE_SERVER_CONFIG = OAuth2ResourceServerConfiguration.class; - - private static final Class AUTHORIZATION_SERVER_CONFIG = OAuth2AuthorizationServerConfiguration.class; - - private AnnotationConfigServletWebServerApplicationContext context; - - @Test - public void testDefaultConfiguration() { - this.context = new AnnotationConfigServletWebServerApplicationContext(); - this.context.register(AuthorizationAndResourceServerConfiguration.class, - MinimalSecureWebApplication.class); - this.context.refresh(); - 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); - UserApprovalHandler handler = (UserApprovalHandler) ReflectionTestUtils - .getField(endpoint, "userApprovalHandler"); - ClientDetailsService clientDetailsService = this.context - .getBean(ClientDetailsService.class); - ClientDetails clientDetails = clientDetailsService - .loadClientByClientId(config.getClientId()); - assertThat(AopUtils.isJdkDynamicProxy(clientDetailsService)).isTrue(); - assertThat(AopUtils.getTargetClass(clientDetailsService).getName()) - .isEqualTo(InMemoryClientDetailsService.class.getName()); - assertThat(handler).isInstanceOf(ApprovalStoreUserApprovalHandler.class); - assertThat(clientDetails).isEqualTo(config); - verifyAuthentication(config); - assertThat(this.context.getBeanNamesForType(OAuth2RestOperations.class)) - .isEmpty(); - } - - @Test - public void methodSecurityExpressionHandlerIsConfiguredWithRoleHierarchyFromTheContext() { - this.context = new AnnotationConfigServletWebServerApplicationContext(); - this.context.register(RoleHierarchyConfiguration.class, - AuthorizationAndResourceServerConfiguration.class, - MinimalSecureWebApplication.class); - this.context.refresh(); - PreInvocationAuthorizationAdvice advice = this.context - .getBean(PreInvocationAuthorizationAdvice.class); - MethodSecurityExpressionHandler expressionHandler = (MethodSecurityExpressionHandler) ReflectionTestUtils - .getField(advice, "expressionHandler"); - RoleHierarchy roleHierarchy = (RoleHierarchy) ReflectionTestUtils - .getField(expressionHandler, "roleHierarchy"); - assertThat(roleHierarchy).isSameAs(this.context.getBean(RoleHierarchy.class)); - } - - @Test - public void methodSecurityExpressionHandlerIsConfiguredWithPermissionEvaluatorFromTheContext() { - this.context = new AnnotationConfigServletWebServerApplicationContext(); - this.context.register(PermissionEvaluatorConfiguration.class, - AuthorizationAndResourceServerConfiguration.class, - MinimalSecureWebApplication.class); - this.context.refresh(); - PreInvocationAuthorizationAdvice advice = this.context - .getBean(PreInvocationAuthorizationAdvice.class); - MethodSecurityExpressionHandler expressionHandler = (MethodSecurityExpressionHandler) ReflectionTestUtils - .getField(advice, "expressionHandler"); - PermissionEvaluator permissionEvaluator = (PermissionEvaluator) ReflectionTestUtils - .getField(expressionHandler, "permissionEvaluator"); - assertThat(permissionEvaluator) - .isSameAs(this.context.getBean(PermissionEvaluator.class)); - } - - @Test - public void testEnvironmentalOverrides() { - this.context = new AnnotationConfigServletWebServerApplicationContext(); - TestPropertyValues - .of("security.oauth2.client.clientId:myclientid", - "security.oauth2.client.clientSecret:mysecret", - "security.oauth2.client.autoApproveScopes:read,write", - "security.oauth2.client.accessTokenValiditySeconds:40", - "security.oauth2.client.refreshTokenValiditySeconds:80") - .applyTo(this.context); - this.context.register(AuthorizationAndResourceServerConfiguration.class, - MinimalSecureWebApplication.class); - this.context.refresh(); - ClientDetails config = this.context.getBean(ClientDetails.class); - assertThat(config.getClientId()).isEqualTo("myclientid"); - assertThat(config.getClientSecret()).isEqualTo("mysecret"); - assertThat(config.isAutoApprove("read")).isTrue(); - assertThat(config.isAutoApprove("write")).isTrue(); - assertThat(config.isAutoApprove("foo")).isFalse(); - assertThat(config.getAccessTokenValiditySeconds()).isEqualTo(40); - assertThat(config.getRefreshTokenValiditySeconds()).isEqualTo(80); - verifyAuthentication(config); - } - - @Test - public void testDisablingResourceServer() { - this.context = new AnnotationConfigServletWebServerApplicationContext(); - this.context.register(AuthorizationServerConfiguration.class, - MinimalSecureWebApplication.class); - this.context.refresh(); - assertThat(countBeans(RESOURCE_SERVER_CONFIG)).isEqualTo(0); - assertThat(countBeans(AUTHORIZATION_SERVER_CONFIG)).isEqualTo(1); - } - - @Test - public void testClientIsNotResourceServer() { - this.context = new AnnotationConfigServletWebServerApplicationContext(); - this.context.register(ClientConfiguration.class, - MinimalSecureWebApplication.class); - this.context.refresh(); - assertThat(countBeans(RESOURCE_SERVER_CONFIG)).isEqualTo(0); - assertThat(countBeans(AUTHORIZATION_SERVER_CONFIG)).isEqualTo(0); - // Scoped target and proxy: - assertThat(countBeans(OAuth2ClientContext.class)).isEqualTo(2); - } - - @Test - public void testCanUseClientCredentials() { - this.context = new AnnotationConfigServletWebServerApplicationContext(); - this.context.register(TestSecurityConfiguration.class, - MinimalSecureWebApplication.class); - TestPropertyValues - .of("security.oauth2.client.clientId=client", - "security.oauth2.client.grantType=client_credentials") - .applyTo(this.context); - ConfigurationPropertySources.attach(this.context.getEnvironment()); - this.context.refresh(); - OAuth2ClientContext bean = this.context.getBean(OAuth2ClientContext.class); - assertThat(bean.getAccessTokenRequest()).isNotNull(); - assertThat(countBeans(ClientCredentialsResourceDetails.class)).isEqualTo(1); - assertThat(countBeans(OAuth2ClientContext.class)).isEqualTo(1); - } - - @Test - public void testCanUseClientCredentialsWithEnableOAuth2Client() { - this.context = new AnnotationConfigServletWebServerApplicationContext(); - this.context.register(ClientConfiguration.class, - MinimalSecureWebApplication.class); - TestPropertyValues - .of("security.oauth2.client.clientId=client", - "security.oauth2.client.grantType=client_credentials") - .applyTo(this.context); - ConfigurationPropertySources.attach(this.context.getEnvironment()); - this.context.refresh(); - // The primary context is fine (not session scoped): - OAuth2ClientContext bean = this.context.getBean(OAuth2ClientContext.class); - assertThat(bean.getAccessTokenRequest()).isNotNull(); - assertThat(countBeans(ClientCredentialsResourceDetails.class)).isEqualTo(1); - // Kind of a bug (should ideally be 1), but the cause is in Spring OAuth2 (there - // is no need for the extra session-scoped bean). What this test proves is that - // even if the user screws up and does @EnableOAuth2Client for client credentials, - // it will still just about work (because of the @Primary annotation on the - // Boot-created instance of OAuth2ClientContext). - assertThat(countBeans(OAuth2ClientContext.class)).isEqualTo(2); - } - - @Test - public void testClientIsNotAuthCode() { - AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); - context.register(MinimalSecureNonWebApplication.class); - TestPropertyValues.of("security.oauth2.client.clientId=client").applyTo(context); - context.refresh(); - assertThat(countBeans(context, ClientCredentialsResourceDetails.class)) - .isEqualTo(1); - context.close(); - } - - @Test - public void testDisablingAuthorizationServer() { - this.context = new AnnotationConfigServletWebServerApplicationContext(); - this.context.register(ResourceServerConfiguration.class, - MinimalSecureWebApplication.class); - TestPropertyValues.of("security.oauth2.resource.jwt.keyValue:DEADBEEF") - .applyTo(this.context); - ConfigurationPropertySources.attach(this.context.getEnvironment()); - this.context.refresh(); - assertThat(countBeans(RESOURCE_SERVER_CONFIG)).isEqualTo(1); - assertThat(countBeans(AUTHORIZATION_SERVER_CONFIG)).isEqualTo(0); - assertThat(countBeans(UserApprovalHandler.class)).isEqualTo(0); - assertThat(countBeans(DefaultTokenServices.class)).isEqualTo(1); - } - - @Test - public void testResourceServerOverride() { - this.context = new AnnotationConfigServletWebServerApplicationContext(); - this.context.register(AuthorizationAndResourceServerConfiguration.class, - CustomResourceServer.class, MinimalSecureWebApplication.class); - this.context.refresh(); - ClientDetails config = this.context.getBean(ClientDetails.class); - assertThat(countBeans(AUTHORIZATION_SERVER_CONFIG)).isEqualTo(1); - assertThat(countBeans(CustomResourceServer.class)).isEqualTo(1); - assertThat(countBeans(RESOURCE_SERVER_CONFIG)).isEqualTo(1); - verifyAuthentication(config); - } - - @Test - public void testAuthorizationServerOverride() { - this.context = new AnnotationConfigServletWebServerApplicationContext(); - TestPropertyValues.of("security.oauth2.resourceId:resource-id") - .applyTo(this.context); - this.context.register(AuthorizationAndResourceServerConfiguration.class, - CustomAuthorizationServer.class, MinimalSecureWebApplication.class); - this.context.refresh(); - BaseClientDetails config = new BaseClientDetails(); - config.setClientId("client"); - config.setClientSecret("secret"); - config.setResourceIds(Arrays.asList("resource-id")); - config.setAuthorizedGrantTypes(Arrays.asList("password")); - config.setAuthorities(AuthorityUtils.commaSeparatedStringToAuthorityList("USER")); - config.setScope(Arrays.asList("read")); - assertThat(countBeans(AUTHORIZATION_SERVER_CONFIG)).isEqualTo(0); - assertThat(countBeans(RESOURCE_SERVER_CONFIG)).isEqualTo(1); - verifyAuthentication(config); - } - - @Test - public void testDefaultPrePostSecurityAnnotations() { - this.context = new AnnotationConfigServletWebServerApplicationContext(); - 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()).isEqualTo(1); - assertThat(sources.get(0).getClass().getName()) - .isEqualTo(PrePostAnnotationSecurityMetadataSource.class.getName()); - verifyAuthentication(config); - } - - @Test - public void testClassicSecurityAnnotationOverride() { - this.context = new AnnotationConfigServletWebServerApplicationContext(); - 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()).isEqualTo(1); - assertThat(sources.get(0).getClass().getName()) - .isEqualTo(SecuredAnnotationSecurityMetadataSource.class.getName()); - verifyAuthentication(config, HttpStatus.OK); - } - - @Test - public void testJsr250SecurityAnnotationOverride() { - this.context = new AnnotationConfigServletWebServerApplicationContext(); - 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()).isEqualTo(1); - assertThat(sources.get(0).getClass().getName()) - .isEqualTo(Jsr250MethodSecurityMetadataSource.class.getName()); - verifyAuthentication(config, HttpStatus.OK); - } - - @Test - public void testMethodSecurityBackingOff() { - this.context = new AnnotationConfigServletWebServerApplicationContext(); - 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()).isEqualTo(1); - assertThat(sources.get(0).getClass().getName()) - .isEqualTo(PrePostAnnotationSecurityMetadataSource.class.getName()); - } - - @Test - public void resourceServerConditionWhenJwkConfigurationPresentShouldMatch() - throws Exception { - this.context = new AnnotationConfigServletWebServerApplicationContext(); - TestPropertyValues - .of("security.oauth2.resource.jwk.key-set-uri:http://my-auth-server/token_keys") - .applyTo(this.context); - this.context.register(ResourceServerConfiguration.class, - MinimalSecureWebApplication.class); - this.context.refresh(); - assertThat(countBeans(RESOURCE_SERVER_CONFIG)).isEqualTo(1); - } - - /** - * Connect to the oauth service, get a token, and then attempt some operations using - * it. - * @param config the client details. - */ - private void verifyAuthentication(ClientDetails config) { - verifyAuthentication(config, HttpStatus.FORBIDDEN); - } - - private void verifyAuthentication(ClientDetails config, HttpStatus finalStatus) { - String baseUrl = "http://localhost:" + this.context.getWebServer().getPort(); - TestRestTemplate rest = new TestRestTemplate(); - // First, verify the web endpoint can't be reached - assertEndpointUnauthorized(baseUrl, rest); - // Since we can't reach it, need to collect an authorization token - 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).isEqualTo("bearer"); - assertThat(scope).isEqualTo("\"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()).isEqualTo(HttpStatus.OK); - assertThat(securedResponse.getBody()).isEqualTo( - "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()).isEqualTo(finalStatus); - } - - private HttpHeaders getHeaders(ClientDetails config) { - HttpHeaders headers = new HttpHeaders(); - String token = new String(Base64.getEncoder().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; - } - - private void assertEndpointUnauthorized(String baseUrl, TestRestTemplate rest) { - URI uri = URI.create(baseUrl + "/secured"); - ResponseEntity entity = rest - .exchange(new RequestEntity(HttpMethod.GET, uri), String.class); - assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.UNAUTHORIZED); - } - - private int countBeans(Class type) { - return countBeans(this.context, type); - } - - private int countBeans(ApplicationContext context, Class type) { - return context.getBeanNamesForType(type).length; - } - - @Configuration - @Import({ UseFreePortEmbeddedContainerConfiguration.class, - SecurityAutoConfiguration.class, DispatcherServletAutoConfiguration.class, - OAuth2AutoConfiguration.class, WebMvcAutoConfiguration.class, - HttpMessageConvertersAutoConfiguration.class }) - protected static class MinimalSecureWebApplication { - - } - - @Configuration - @Import({ SecurityAutoConfiguration.class, OAuth2AutoConfiguration.class }) - protected static class MinimalSecureNonWebApplication { - - } - - @Configuration - protected static class TestSecurityConfiguration - extends WebSecurityConfigurerAdapter { - - @Override - @Bean - public AuthenticationManager authenticationManagerBean() throws Exception { - return super.authenticationManagerBean(); - } - - @Autowired - public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { - auth.inMemoryAuthentication().withUser("foo").password("bar").roles("USER"); - } - - @Bean - TestWebApp testWebApp() { - return new TestWebApp(); - } - - } - - @Configuration - @EnableOAuth2Client - protected static class ClientConfiguration extends TestSecurityConfiguration { - - } - - @Configuration - @EnableAuthorizationServer - @EnableResourceServer - @EnableGlobalMethodSecurity(prePostEnabled = true) - protected static class AuthorizationAndResourceServerConfiguration - extends TestSecurityConfiguration { - - } - - @Configuration - @EnableAuthorizationServer - @EnableResourceServer - @EnableGlobalMethodSecurity(securedEnabled = true) - protected static class SecuredEnabledConfiguration extends TestSecurityConfiguration { - - } - - @Configuration - @EnableAuthorizationServer - @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 - protected static class TestWebApp { - - @GetMapping("/securedFind") - @PreAuthorize("#oauth2.hasScope('read')") - public String secureFind() { - return "You reached an endpoint secured by Spring Security OAuth2"; - } - - @PostMapping("/securedSave") - @PreAuthorize("#oauth2.hasScope('write')") - public String secureSave() { - return "You reached an endpoint secured by Spring Security OAuth2"; - } - - } - - @Configuration - protected static class UseFreePortEmbeddedContainerConfiguration { - - @Bean - TomcatServletWebServerFactory webServerFactory() { - return new TomcatServletWebServerFactory(0); - } - - } - - @Configuration - @EnableResourceServer - protected static class CustomResourceServer extends ResourceServerConfigurerAdapter { - - private final ResourceServerProperties config; - - protected CustomResourceServer(ResourceServerProperties config) { - this.config = config; - } - - @Override - public void configure(ResourceServerSecurityConfigurer resources) - throws Exception { - if (this.config.getId() != null) { - resources.resourceId(this.config.getId()); - } - } - - @Override - public void configure(HttpSecurity http) throws Exception { - http.authorizeRequests().anyRequest().authenticated().and().httpBasic().and() - .csrf().disable(); - } - - } - - @Configuration - @EnableAuthorizationServer - protected static class CustomAuthorizationServer - extends AuthorizationServerConfigurerAdapter { - - private final AuthenticationManager authenticationManager; - - protected CustomAuthorizationServer(AuthenticationManager authenticationManager) { - this.authenticationManager = authenticationManager; - } - - @Bean - public TokenStore tokenStore() { - return new InMemoryTokenStore(); - } - - @Bean - public ApprovalStore approvalStore(final TokenStore tokenStore) { - TokenApprovalStore approvalStore = new TokenApprovalStore(); - approvalStore.setTokenStore(tokenStore); - return approvalStore; - } - - @Override - public void configure(ClientDetailsServiceConfigurer clients) throws Exception { - clients.inMemory().withClient("client").secret("secret") - .resourceIds("resource-id").authorizedGrantTypes("password") - .authorities("USER").scopes("read") - .redirectUris("http://localhost:8080"); - } - - @Override - public void configure(AuthorizationServerEndpointsConfigurer endpoints) - throws Exception { - endpoints.tokenStore(tokenStore()) - .authenticationManager(this.authenticationManager); - } - - } - - @Configuration - @EnableGlobalMethodSecurity(prePostEnabled = true) - protected static class CustomMethodSecurity - extends GlobalMethodSecurityConfiguration { - - @Override - protected MethodSecurityExpressionHandler createExpressionHandler() { - return new OAuth2MethodSecurityExpressionHandler(); - } - - } - - @Configuration - protected static class RoleHierarchyConfiguration { - - @Bean - public RoleHierarchy roleHierarchy() { - return mock(RoleHierarchy.class); - } - - } - - @Configuration - protected static class PermissionEvaluatorConfiguration { - - @Bean - public PermissionEvaluator permissionEvaluator() { - return mock(PermissionEvaluator.class); - } - - } - -} diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/client/OAuth2RestOperationsConfigurationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/client/OAuth2RestOperationsConfigurationTests.java deleted file mode 100644 index 07eb00b19a..0000000000 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/client/OAuth2RestOperationsConfigurationTests.java +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Copyright 2012-2017 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.boot.autoconfigure.security.oauth2.client; - -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExpectedException; - -import org.springframework.beans.factory.NoSuchBeanDefinitionException; -import org.springframework.boot.WebApplicationType; -import org.springframework.boot.autoconfigure.security.SecurityProperties; -import org.springframework.boot.autoconfigure.web.servlet.MockServletWebServerFactory; -import org.springframework.boot.builder.SpringApplicationBuilder; -import org.springframework.boot.test.util.TestPropertyValues; -import org.springframework.context.ConfigurableApplicationContext; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Import; -import org.springframework.core.env.ConfigurableEnvironment; -import org.springframework.core.env.StandardEnvironment; -import org.springframework.security.oauth2.client.DefaultOAuth2ClientContext; -import org.springframework.security.oauth2.client.token.grant.client.ClientCredentialsResourceDetails; -import org.springframework.security.oauth2.config.annotation.web.configuration.OAuth2ClientConfiguration; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Tests for {@link OAuth2RestOperationsConfiguration}. - * - * @author Madhura Bhave - */ -public class OAuth2RestOperationsConfigurationTests { - - private ConfigurableApplicationContext context; - - private ConfigurableEnvironment environment = new StandardEnvironment(); - - @Rule - public ExpectedException thrown = ExpectedException.none(); - - @Test - public void clientCredentialsWithClientId() throws Exception { - TestPropertyValues.of("security.oauth2.client.client-id=acme") - .applyTo(this.environment); - initializeContext(OAuth2RestOperationsConfiguration.class, true); - assertThat(this.context.getBean(OAuth2RestOperationsConfiguration.class)) - .isNotNull(); - assertThat(this.context.getBean(ClientCredentialsResourceDetails.class)) - .isNotNull(); - } - - @Test - public void clientCredentialsWithNoClientId() throws Exception { - initializeContext(OAuth2RestOperationsConfiguration.class, true); - assertThat(this.context.getBean(OAuth2RestOperationsConfiguration.class)) - .isNotNull(); - assertThat(this.context.getBean(ClientCredentialsResourceDetails.class)) - .isNotNull(); - } - - @Test - public void requestScopedWithClientId() throws Exception { - TestPropertyValues.of("security.oauth2.client.client-id=acme") - .applyTo(this.environment); - initializeContext(ConfigForRequestScopedConfiguration.class, false); - assertThat(this.context.containsBean("oauth2ClientContext")).isTrue(); - } - - @Test - public void requestScopedWithNoClientId() throws Exception { - initializeContext(ConfigForRequestScopedConfiguration.class, false); - this.thrown.expect(NoSuchBeanDefinitionException.class); - this.context.getBean(DefaultOAuth2ClientContext.class); - } - - @Test - public void sessionScopedWithClientId() throws Exception { - TestPropertyValues.of("security.oauth2.client.client-id=acme") - .applyTo(this.environment); - initializeContext(ConfigForSessionScopedConfiguration.class, false); - assertThat(this.context.containsBean("oauth2ClientContext")).isTrue(); - } - - @Test - public void sessionScopedWithNoClientId() throws Exception { - initializeContext(ConfigForSessionScopedConfiguration.class, false); - this.thrown.expect(NoSuchBeanDefinitionException.class); - this.context.getBean(DefaultOAuth2ClientContext.class); - } - - private void initializeContext(Class configuration, boolean clientCredentials) { - this.context = new SpringApplicationBuilder(configuration) - .environment(this.environment).web(clientCredentials - ? WebApplicationType.NONE : WebApplicationType.SERVLET) - .run(); - } - - @Configuration - @Import({ OAuth2RestOperationsConfiguration.class }) - protected static class WebApplicationConfiguration { - - @Bean - public MockServletWebServerFactory webServerFactory() { - return new MockServletWebServerFactory(); - } - - } - - @Configuration - @Import({ SecurityProperties.class, OAuth2ClientConfiguration.class, - OAuth2RestOperationsConfiguration.class }) - protected static class ConfigForSessionScopedConfiguration - extends WebApplicationConfiguration { - - } - - @Configuration - protected static class ConfigForRequestScopedConfiguration - extends WebApplicationConfiguration { - - } - -} diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/resource/FixedAuthoritiesExtractorTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/resource/FixedAuthoritiesExtractorTests.java deleted file mode 100644 index 8152f628ef..0000000000 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/resource/FixedAuthoritiesExtractorTests.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright 2012-2017 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.boot.autoconfigure.security.oauth2.resource; - -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.Map; - -import org.junit.Test; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Tests for {@link FixedAuthoritiesExtractor}. - * - * @author Dave Syer - */ -public class FixedAuthoritiesExtractorTests { - - private FixedAuthoritiesExtractor extractor = new FixedAuthoritiesExtractor(); - - private Map map = new LinkedHashMap<>(); - - @Test - public void authorities() { - this.map.put("authorities", "ROLE_ADMIN"); - assertThat(this.extractor.extractAuthorities(this.map).toString()) - .isEqualTo("[ROLE_ADMIN]"); - } - - @Test - public void authoritiesCommaSeparated() { - this.map.put("authorities", "ROLE_USER,ROLE_ADMIN"); - assertThat(this.extractor.extractAuthorities(this.map).toString()) - .isEqualTo("[ROLE_USER, ROLE_ADMIN]"); - } - - @Test - public void authoritiesArray() { - this.map.put("authorities", new String[] { "ROLE_USER", "ROLE_ADMIN" }); - assertThat(this.extractor.extractAuthorities(this.map).toString()) - .isEqualTo("[ROLE_USER, ROLE_ADMIN]"); - } - - @Test - public void authoritiesList() { - this.map.put("authorities", Arrays.asList("ROLE_USER", "ROLE_ADMIN")); - assertThat(this.extractor.extractAuthorities(this.map).toString()) - .isEqualTo("[ROLE_USER, ROLE_ADMIN]"); - } - - @Test - public void authoritiesAsListOfMaps() { - this.map.put("authorities", - Arrays.asList(Collections.singletonMap("authority", "ROLE_ADMIN"))); - assertThat(this.extractor.extractAuthorities(this.map).toString()) - .isEqualTo("[ROLE_ADMIN]"); - } - - @Test - public void authoritiesAsListOfMapsWithStandardKey() { - Map map = new LinkedHashMap<>(); - map.put("role", "ROLE_ADMIN"); - map.put("extra", "value"); - this.map.put("authorities", Arrays.asList(map)); - assertThat(this.extractor.extractAuthorities(this.map).toString()) - .isEqualTo("[ROLE_ADMIN]"); - } - - @Test - public void authoritiesAsListOfMapsWithNonStandardKey() { - this.map.put("authorities", - Arrays.asList(Collections.singletonMap("any", "ROLE_ADMIN"))); - assertThat(this.extractor.extractAuthorities(this.map).toString()) - .isEqualTo("[ROLE_ADMIN]"); - } - - @Test - public void authoritiesAsListOfMapsWithMultipleNonStandardKeys() { - Map map = new HashMap<>(); - map.put("any", "ROLE_ADMIN"); - map.put("foo", "bar"); - this.map.put("authorities", Arrays.asList(map)); - assertThat(this.extractor.extractAuthorities(this.map).toString()) - .isEqualTo("[{foo=bar, any=ROLE_ADMIN}]"); - } - -} diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/resource/MultipleResourceServerConfigurationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/resource/MultipleResourceServerConfigurationTests.java deleted file mode 100644 index 5c11bb1ce4..0000000000 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/resource/MultipleResourceServerConfigurationTests.java +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright 2012-2017 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.boot.autoconfigure.security.oauth2.resource; - -import java.util.List; - -import org.junit.After; -import org.junit.Test; - -import org.springframework.boot.autoconfigure.ImportAutoConfiguration; -import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration; -import org.springframework.boot.autoconfigure.security.oauth2.OAuth2AutoConfiguration; -import org.springframework.boot.test.util.TestPropertyValues; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; -import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfiguration; -import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurer; -import org.springframework.web.context.support.AnnotationConfigWebApplicationContext; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Tests for {@link OAuth2ResourceServerConfiguration} when there are multiple - * {@link ResourceServerConfiguration} beans. - * - * @author Dave Syer - */ -public class MultipleResourceServerConfigurationTests { - - private AnnotationConfigWebApplicationContext context; - - @After - public void close() { - if (this.context != null) { - this.context.close(); - } - } - - @Test - public void orderIsUnchangedWhenThereAreMultipleResourceServerConfigurations() { - this.context = new AnnotationConfigWebApplicationContext(); - this.context.register(DoubleResourceConfiguration.class); - TestPropertyValues.of("security.oauth2.resource.tokenInfoUri:http://example.com", - "security.oauth2.client.clientId=acme").applyTo(this.context); - this.context.refresh(); - assertThat(this.context - .getBean("adminResources", ResourceServerConfiguration.class).getOrder()) - .isEqualTo(3); - assertThat(this.context - .getBean("otherResources", ResourceServerConfiguration.class).getOrder()) - .isEqualTo(4); - } - - @ImportAutoConfiguration({ OAuth2AutoConfiguration.class, - PropertyPlaceholderAutoConfiguration.class }) - @EnableWebSecurity - @Configuration - protected static class DoubleResourceConfiguration { - - @Bean - protected ResourceServerConfiguration adminResources() { - - ResourceServerConfiguration resource = new ResourceServerConfiguration() { - // Switch off the Spring Boot @Autowired configurers - @Override - public void setConfigurers(List configurers) { - super.setConfigurers(configurers); - } - }; - resource.setOrder(3); - return resource; - } - - @Bean - protected ResourceServerConfiguration otherResources() { - - ResourceServerConfiguration resource = new ResourceServerConfiguration() { - // Switch off the Spring Boot @Autowired configurers - @Override - public void setConfigurers(List configurers) { - super.setConfigurers(configurers); - } - }; - resource.setOrder(4); - return resource; - } - - } - -} 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 deleted file mode 100644 index 58f15d4c1f..0000000000 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/resource/ResourceServerPropertiesTests.java +++ /dev/null @@ -1,224 +0,0 @@ -/* - * Copyright 2012-2017 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.boot.autoconfigure.security.oauth2.resource; - -import java.util.Map; - -import com.fasterxml.jackson.databind.ObjectMapper; -import org.hamcrest.BaseMatcher; -import org.hamcrest.Description; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExpectedException; - -import org.springframework.beans.factory.ListableBeanFactory; -import org.springframework.validation.BindException; -import org.springframework.validation.Errors; -import org.springframework.validation.FieldError; -import org.springframework.validation.ObjectError; -import org.springframework.web.context.support.StaticWebApplicationContext; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verifyZeroInteractions; - -/** - * Tests for {@link ResourceServerProperties}. - * - * @author Dave Syer - * @author Vedran Pavic - * @author Madhura Bhave - */ -public class ResourceServerPropertiesTests { - - private ResourceServerProperties properties = new ResourceServerProperties("client", - "secret"); - - private Errors errors = mock(Errors.class); - - @Rule - public ExpectedException thrown = ExpectedException.none(); - - @Test - @SuppressWarnings("unchecked") - public void json() throws Exception { - this.properties.getJwt().setKeyUri("http://example.com/token_key"); - ObjectMapper mapper = new ObjectMapper(); - String json = mapper.writeValueAsString(this.properties); - Map value = mapper.readValue(json, Map.class); - Map jwt = (Map) value.get("jwt"); - assertThat(jwt.get("keyUri")).isNotNull(); - } - - @Test - public void validateWhenClientIdNullShouldNotFail() throws Exception { - this.properties = new ResourceServerProperties(null, "secret"); - setListableBeanFactory(); - this.properties.validate(); - verifyZeroInteractions(this.errors); - } - - @Test - public void validateWhenBothJwtAndJwkKeyUrisPresentShouldFail() throws Exception { - this.properties.getJwk().setKeySetUri("http://my-auth-server/token_keys"); - this.properties.getJwt().setKeyUri("http://my-auth-server/token_key"); - setListableBeanFactory(); - this.thrown.expect(IllegalStateException.class); - this.thrown.expect(getMatcher("Only one of jwt.keyUri (or jwt.keyValue) " - + "and jwk.keySetUri should be configured.", null)); - this.properties.validate(); - } - - @Test - public void validateWhenBothJwtKeyValueAndJwkKeyUriPresentShouldFail() - throws Exception { - this.properties.getJwk().setKeySetUri("http://my-auth-server/token_keys"); - this.properties.getJwt().setKeyValue("my-key"); - setListableBeanFactory(); - this.thrown.expect(IllegalStateException.class); - this.thrown.expect(getMatcher("Only one of jwt.keyUri (or jwt.keyValue) " - + "and jwk.keySetUri should be configured.", null)); - this.properties.validate(); - } - - @Test - public void validateWhenJwkKeySetUriProvidedShouldSucceed() throws Exception { - this.properties.getJwk().setKeySetUri("http://my-auth-server/token_keys"); - setListableBeanFactory(); - this.properties.validate(); - verifyZeroInteractions(this.errors); - } - - @Test - public void validateWhenKeyValuePresentShouldSucceed() throws Exception { - this.properties.getJwt().setKeyValue("my-key"); - setListableBeanFactory(); - this.properties.validate(); - verifyZeroInteractions(this.errors); - } - - @Test - public void validateWhenKeysUriOrValuePresentAndUserInfoAbsentShouldNotFail() - throws Exception { - this.properties = new ResourceServerProperties("client", ""); - this.properties.getJwk().setKeySetUri("http://my-auth-server/token_keys"); - setListableBeanFactory(); - this.properties.validate(); - verifyZeroInteractions(this.errors); - } - - @Test - public void validateWhenKeyConfigAbsentAndInfoUrisNotConfiguredShouldFail() - throws Exception { - setListableBeanFactory(); - this.thrown.expect(IllegalStateException.class); - this.thrown.expect(getMatcher("Missing tokenInfoUri and userInfoUri and there" - + " is no JWT verifier key", "tokenInfoUri")); - this.properties.validate(); - } - - @Test - public void validateWhenTokenUriConfiguredShouldNotFail() throws Exception { - this.properties.setTokenInfoUri("http://my-auth-server/userinfo"); - setListableBeanFactory(); - this.properties.validate(); - verifyZeroInteractions(this.errors); - } - - @Test - public void validateWhenUserInfoUriConfiguredShouldNotFail() throws Exception { - this.properties.setUserInfoUri("http://my-auth-server/userinfo"); - setListableBeanFactory(); - this.properties.validate(); - verifyZeroInteractions(this.errors); - } - - @Test - public void validateWhenTokenUriPreferredAndClientSecretAbsentShouldFail() - throws Exception { - this.properties = new ResourceServerProperties("client", ""); - this.properties.setTokenInfoUri("http://my-auth-server/check_token"); - this.properties.setUserInfoUri("http://my-auth-server/userinfo"); - setListableBeanFactory(); - this.thrown.expect(IllegalStateException.class); - this.thrown.expect(getMatcher("Missing client secret", "clientSecret")); - this.properties.validate(); - } - - @Test - public void validateWhenTokenUriAbsentAndClientSecretAbsentShouldNotFail() - throws Exception { - this.properties = new ResourceServerProperties("client", ""); - this.properties.setUserInfoUri("http://my-auth-server/userinfo"); - setListableBeanFactory(); - this.properties.validate(); - verifyZeroInteractions(this.errors); - } - - @Test - public void validateWhenTokenUriNotPreferredAndClientSecretAbsentShouldNotFail() - throws Exception { - this.properties = new ResourceServerProperties("client", ""); - this.properties.setPreferTokenInfo(false); - this.properties.setTokenInfoUri("http://my-auth-server/check_token"); - this.properties.setUserInfoUri("http://my-auth-server/userinfo"); - setListableBeanFactory(); - this.properties.validate(); - verifyZeroInteractions(this.errors); - } - - private void setListableBeanFactory() { - ListableBeanFactory beanFactory = new StaticWebApplicationContext() { - - @Override - public String[] getBeanNamesForType(Class type, - boolean includeNonSingletons, boolean allowEagerInit) { - if (type.isAssignableFrom( - ResourceServerTokenServicesConfiguration.class)) { - return new String[] { "ResourceServerTokenServicesConfiguration" }; - } - return new String[0]; - } - - }; - this.properties.setBeanFactory(beanFactory); - } - - private BaseMatcher getMatcher(String message, String field) { - return new BaseMatcher() { - - @Override - public void describeTo(Description description) { - - } - - @Override - public boolean matches(Object item) { - BindException ex = (BindException) ((Exception) item).getCause(); - ObjectError error = ex.getAllErrors().get(0); - boolean messageMatches = message.equals(error.getDefaultMessage()); - if (field == null) { - return messageMatches; - } - String fieldErrors = ((FieldError) error).getField(); - return messageMatches && fieldErrors.equals(field); - } - - }; - } - -} 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 deleted file mode 100644 index 952340d53d..0000000000 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/resource/ResourceServerTokenServicesConfigurationTests.java +++ /dev/null @@ -1,417 +0,0 @@ -/* - * Copyright 2012-2017 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.boot.autoconfigure.security.oauth2.resource; - -import org.junit.After; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExpectedException; - -import org.springframework.beans.factory.NoSuchBeanDefinitionException; -import org.springframework.beans.factory.config.BeanDefinition; -import org.springframework.beans.factory.support.BeanDefinitionRegistry; -import org.springframework.boot.WebApplicationType; -import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration; -import org.springframework.boot.autoconfigure.security.oauth2.OAuth2ClientProperties; -import org.springframework.boot.autoconfigure.security.oauth2.client.OAuth2RestOperationsConfiguration; -import org.springframework.boot.autoconfigure.social.FacebookAutoConfiguration; -import org.springframework.boot.autoconfigure.social.SocialWebAutoConfiguration; -import org.springframework.boot.autoconfigure.web.servlet.MockServletWebServerFactory; -import org.springframework.boot.builder.SpringApplicationBuilder; -import org.springframework.boot.context.properties.EnableConfigurationProperties; -import org.springframework.boot.test.util.TestPropertyValues; -import org.springframework.boot.web.servlet.server.ServletWebServerFactory; -import org.springframework.context.ConfigurableApplicationContext; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Import; -import org.springframework.core.env.ConfigurableEnvironment; -import org.springframework.core.env.StandardEnvironment; -import org.springframework.http.HttpStatus; -import org.springframework.http.MediaType; -import org.springframework.mock.http.client.MockClientHttpResponse; -import org.springframework.security.core.authority.AuthorityUtils; -import org.springframework.security.oauth2.client.OAuth2RestTemplate; -import org.springframework.security.oauth2.client.token.grant.code.AuthorizationCodeResourceDetails; -import org.springframework.security.oauth2.provider.token.DefaultTokenServices; -import org.springframework.security.oauth2.provider.token.RemoteTokenServices; -import org.springframework.security.oauth2.provider.token.TokenStore; -import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter; -import org.springframework.security.oauth2.provider.token.store.JwtTokenStore; -import org.springframework.security.oauth2.provider.token.store.jwk.JwkTokenStore; -import org.springframework.social.connect.ConnectionFactoryLocator; -import org.springframework.stereotype.Component; -import org.springframework.web.client.RestTemplate; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.mock; - -/** - * Tests for {@link ResourceServerTokenServicesConfiguration}. - * - * @author Dave Syer - * @author Madhura Bhave - * @author Eddú Meléndez - */ -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(); - - @Rule - public ExpectedException thrown = ExpectedException.none(); - - @After - public void close() { - if (this.context != null) { - this.context.close(); - } - } - - @Test - public void useRemoteTokenServices() { - TestPropertyValues.of("security.oauth2.resource.tokenInfoUri:http://example.com") - .applyTo(this.environment); - this.context = new SpringApplicationBuilder(ResourceConfiguration.class) - .environment(this.environment).web(WebApplicationType.NONE).run(); - RemoteTokenServices services = this.context.getBean(RemoteTokenServices.class); - assertThat(services).isNotNull(); - } - - @Test - public void switchToUserInfo() { - TestPropertyValues.of("security.oauth2.resource.userInfoUri:http://example.com") - .applyTo(this.environment); - this.context = new SpringApplicationBuilder(ResourceConfiguration.class) - .environment(this.environment).web(WebApplicationType.NONE).run(); - UserInfoTokenServices services = this.context - .getBean(UserInfoTokenServices.class); - assertThat(services).isNotNull(); - } - - @Test - public void userInfoWithAuthorities() { - TestPropertyValues.of("security.oauth2.resource.userInfoUri:http://example.com") - .applyTo(this.environment); - this.context = new SpringApplicationBuilder(AuthoritiesConfiguration.class) - .environment(this.environment).web(WebApplicationType.NONE).run(); - UserInfoTokenServices services = this.context - .getBean(UserInfoTokenServices.class); - assertThat(services).isNotNull(); - assertThat(services).extracting("authoritiesExtractor") - .containsExactly(this.context.getBean(AuthoritiesExtractor.class)); - } - - @Test - public void userInfoWithPrincipal() { - TestPropertyValues.of("security.oauth2.resource.userInfoUri:http://example.com") - .applyTo(this.environment); - this.context = new SpringApplicationBuilder(PrincipalConfiguration.class) - .environment(this.environment).web(WebApplicationType.NONE).run(); - UserInfoTokenServices services = this.context - .getBean(UserInfoTokenServices.class); - assertThat(services).isNotNull(); - assertThat(services).extracting("principalExtractor") - .containsExactly(this.context.getBean(PrincipalExtractor.class)); - } - - @Test - public void userInfoWithClient() { - TestPropertyValues.of("security.oauth2.client.client-id=acme", - "security.oauth2.resource.userInfoUri:http://example.com", - "server.port=-1", "debug=true").applyTo(this.environment); - this.context = new SpringApplicationBuilder(ResourceNoClientConfiguration.class) - .environment(this.environment).web(WebApplicationType.SERVLET).run(); - BeanDefinition bean = ((BeanDefinitionRegistry) this.context) - .getBeanDefinition("scopedTarget.oauth2ClientContext"); - assertThat(bean.getScope()).isEqualTo("request"); - } - - @Test - public void preferUserInfo() { - TestPropertyValues - .of("security.oauth2.resource.userInfoUri:http://example.com", - "security.oauth2.resource.tokenInfoUri:http://example.com", - "security.oauth2.resource.preferTokenInfo:false") - .applyTo(this.environment); - this.context = new SpringApplicationBuilder(ResourceConfiguration.class) - .environment(this.environment).web(WebApplicationType.NONE).run(); - UserInfoTokenServices services = this.context - .getBean(UserInfoTokenServices.class); - assertThat(services).isNotNull(); - } - - @Test - public void userInfoWithCustomizer() { - TestPropertyValues - .of("security.oauth2.resource.userInfoUri:http://example.com", - "security.oauth2.resource.tokenInfoUri:http://example.com", - "security.oauth2.resource.preferTokenInfo:false") - .applyTo(this.environment); - this.context = new SpringApplicationBuilder(ResourceConfiguration.class, - Customizer.class).environment(this.environment) - .web(WebApplicationType.NONE).run(); - UserInfoTokenServices services = this.context - .getBean(UserInfoTokenServices.class); - assertThat(services).isNotNull(); - } - - @Test - public void switchToJwt() { - TestPropertyValues.of("security.oauth2.resource.jwt.keyValue=FOOBAR") - .applyTo(this.environment); - this.context = new SpringApplicationBuilder(ResourceConfiguration.class) - .environment(this.environment).web(WebApplicationType.NONE).run(); - DefaultTokenServices services = this.context.getBean(DefaultTokenServices.class); - assertThat(services).isNotNull(); - this.thrown.expect(NoSuchBeanDefinitionException.class); - this.context.getBean(RemoteTokenServices.class); - } - - @Test - public void asymmetricJwt() { - TestPropertyValues.of("security.oauth2.resource.jwt.keyValue=" + PUBLIC_KEY) - .applyTo(this.environment); - this.context = new SpringApplicationBuilder(ResourceConfiguration.class) - .environment(this.environment).web(WebApplicationType.NONE).run(); - DefaultTokenServices services = this.context.getBean(DefaultTokenServices.class); - assertThat(services).isNotNull(); - } - - @Test - public void jwkConfiguration() throws Exception { - TestPropertyValues - .of("security.oauth2.resource.jwk.key-set-uri=http://my-auth-server/token_keys") - .applyTo(this.environment); - this.context = new SpringApplicationBuilder(ResourceConfiguration.class) - .environment(this.environment).web(WebApplicationType.NONE).run(); - DefaultTokenServices services = this.context.getBean(DefaultTokenServices.class); - assertThat(services).isNotNull(); - this.thrown.expect(NoSuchBeanDefinitionException.class); - this.context.getBean(RemoteTokenServices.class); - } - - @Test - public void springSocialUserInfo() { - TestPropertyValues - .of("security.oauth2.resource.userInfoUri:http://example.com", - "spring.social.facebook.app-id=foo", - "spring.social.facebook.app-secret=bar") - .applyTo(this.environment); - this.context = new SpringApplicationBuilder(SocialResourceConfiguration.class) - .environment(this.environment).web(WebApplicationType.SERVLET).run(); - ConnectionFactoryLocator connectionFactory = this.context - .getBean(ConnectionFactoryLocator.class); - assertThat(connectionFactory).isNotNull(); - SpringSocialTokenServices services = this.context - .getBean(SpringSocialTokenServices.class); - assertThat(services).isNotNull(); - } - - @Test - public void customUserInfoRestTemplateFactory() { - TestPropertyValues.of("security.oauth2.resource.userInfoUri:http://example.com") - .applyTo(this.environment); - this.context = new SpringApplicationBuilder( - CustomUserInfoRestTemplateFactory.class, ResourceConfiguration.class) - .environment(this.environment).web(WebApplicationType.NONE).run(); - assertThat(this.context.getBeansOfType(UserInfoRestTemplateFactory.class)) - .hasSize(1); - assertThat(this.context.getBean(UserInfoRestTemplateFactory.class)) - .isInstanceOf(CustomUserInfoRestTemplateFactory.class); - } - - @Test - public void jwtAccessTokenConverterIsConfiguredWhenKeyUriIsProvided() { - TestPropertyValues - .of("security.oauth2.resource.jwt.key-uri=http://localhost:12345/banana") - .applyTo(this.environment); - this.context = new SpringApplicationBuilder(ResourceConfiguration.class, - JwtAccessTokenConverterRestTemplateCustomizerConfiguration.class) - .environment(this.environment).web(WebApplicationType.NONE).run(); - assertThat(this.context.getBeansOfType(JwtAccessTokenConverter.class)).hasSize(1); - } - - @Test - public void jwkTokenStoreShouldBeConditionalOnMissingBean() throws Exception { - TestPropertyValues - .of("security.oauth2.resource.jwk.key-set-uri=http://my-auth-server/token_keys") - .applyTo(this.environment); - this.context = new SpringApplicationBuilder(JwkTokenStoreConfiguration.class, - ResourceConfiguration.class).environment(this.environment) - .web(WebApplicationType.NONE).run(); - assertThat(this.context.getBeansOfType(JwkTokenStore.class)).hasSize(1); - } - - @Test - public void jwtTokenStoreShouldBeConditionalOnMissingBean() throws Exception { - TestPropertyValues.of("security.oauth2.resource.jwt.keyValue=" + PUBLIC_KEY) - .applyTo(this.environment); - this.context = new SpringApplicationBuilder(JwtTokenStoreConfiguration.class, - ResourceConfiguration.class).environment(this.environment) - .web(WebApplicationType.NONE).run(); - assertThat(this.context.getBeansOfType(JwtTokenStore.class)).hasSize(1); - } - - @Configuration - @Import({ ResourceServerTokenServicesConfiguration.class, - ResourceServerPropertiesConfiguration.class, - PropertyPlaceholderAutoConfiguration.class }) - @EnableConfigurationProperties(OAuth2ClientProperties.class) - protected static class ResourceConfiguration { - - } - - @Configuration - protected static class AuthoritiesConfiguration extends ResourceConfiguration { - - @Bean - AuthoritiesExtractor authoritiesExtractor() { - return (map) -> AuthorityUtils - .commaSeparatedStringToAuthorityList("ROLE_ADMIN"); - } - - } - - @Configuration - protected static class PrincipalConfiguration extends ResourceConfiguration { - - @Bean - PrincipalExtractor principalExtractor() { - return (map) -> "boot"; - } - - } - - @Import({ OAuth2RestOperationsConfiguration.class }) - protected static class ResourceNoClientConfiguration extends ResourceConfiguration { - - @Bean - public MockServletWebServerFactory webServerFactory() { - return new MockServletWebServerFactory(); - } - - } - - @Configuration - protected static class ResourceServerPropertiesConfiguration { - - private OAuth2ClientProperties credentials; - - public ResourceServerPropertiesConfiguration(OAuth2ClientProperties credentials) { - this.credentials = credentials; - } - - @Bean - public ResourceServerProperties resourceServerProperties() { - return new ResourceServerProperties(this.credentials.getClientId(), - this.credentials.getClientSecret()); - } - - } - - @Import({ FacebookAutoConfiguration.class, SocialWebAutoConfiguration.class }) - protected static class SocialResourceConfiguration extends ResourceConfiguration { - - @Bean - public ServletWebServerFactory webServerFactory() { - return mock(ServletWebServerFactory.class); - } - - } - - @Component - protected static class Customizer implements UserInfoRestTemplateCustomizer { - - @Override - public void customize(OAuth2RestTemplate template) { - template.getInterceptors() - .add((request, body, execution) -> execution.execute(request, body)); - } - - } - - @Component - protected static class CustomUserInfoRestTemplateFactory - implements UserInfoRestTemplateFactory { - - private final OAuth2RestTemplate restTemplate = new OAuth2RestTemplate( - new AuthorizationCodeResourceDetails()); - - @Override - public OAuth2RestTemplate getUserInfoRestTemplate() { - return this.restTemplate; - } - - } - - @Configuration - static class JwtAccessTokenConverterRestTemplateCustomizerConfiguration { - - @Bean - public JwtAccessTokenConverterRestTemplateCustomizer restTemplateCustomizer() { - return new MockRestCallCustomizer(); - } - - } - - @Configuration - static class JwtTokenStoreConfiguration { - - @Bean - public TokenStore tokenStore(JwtAccessTokenConverter jwtTokenEnhancer) { - return new JwtTokenStore(jwtTokenEnhancer); - } - - } - - @Configuration - static class JwkTokenStoreConfiguration { - - @Bean - public TokenStore tokenStore() { - return new JwkTokenStore("http://my.key-set.uri"); - } - - } - - private static class MockRestCallCustomizer - implements JwtAccessTokenConverterRestTemplateCustomizer { - - @Override - public void customize(RestTemplate template) { - template.getInterceptors().add((request, body, execution) -> { - String payload = "{\"value\":\"FOO\"}"; - MockClientHttpResponse response = new MockClientHttpResponse( - payload.getBytes(), HttpStatus.OK); - response.getHeaders().setContentType(MediaType.APPLICATION_JSON); - return response; - }); - } - - } - -} diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/resource/UserInfoTokenServicesRefreshTokenTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/resource/UserInfoTokenServicesRefreshTokenTests.java deleted file mode 100644 index 290ed02f72..0000000000 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/resource/UserInfoTokenServicesRefreshTokenTests.java +++ /dev/null @@ -1,150 +0,0 @@ -/* - * Copyright 2012-2017 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.boot.autoconfigure.security.oauth2.resource; - -import java.util.Date; - -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExpectedException; -import org.junit.runner.RunWith; - -import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration; -import org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration; -import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration; -import org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration; -import org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; -import org.springframework.boot.web.server.LocalServerPort; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Import; -import org.springframework.http.HttpStatus; -import org.springframework.security.oauth2.client.DefaultOAuth2ClientContext; -import org.springframework.security.oauth2.client.OAuth2ClientContext; -import org.springframework.security.oauth2.client.OAuth2RestTemplate; -import org.springframework.security.oauth2.client.resource.OAuth2ProtectedResourceDetails; -import org.springframework.security.oauth2.client.token.grant.code.AuthorizationCodeResourceDetails; -import org.springframework.security.oauth2.common.DefaultExpiringOAuth2RefreshToken; -import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken; -import org.springframework.security.oauth2.common.exceptions.InvalidTokenException; -import org.springframework.test.annotation.DirtiesContext; -import org.springframework.test.context.junit4.SpringRunner; -import org.springframework.web.bind.annotation.ExceptionHandler; -import org.springframework.web.bind.annotation.RequestHeader; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.ResponseStatus; -import org.springframework.web.bind.annotation.RestController; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Tests for {@link UserInfoTokenServices}. - * - * @author Dave Syer - */ -@RunWith(SpringRunner.class) -@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, properties = { - "security.oauth2.resource.userInfoUri:http://example.com", - "security.oauth2.client.clientId=foo" }) -@DirtiesContext -public class UserInfoTokenServicesRefreshTokenTests { - - @Rule - public ExpectedException expected = ExpectedException.none(); - - @LocalServerPort - private int port; - - private UserInfoTokenServices services; - - @Before - public void init() { - this.services = new UserInfoTokenServices( - "http://localhost:" + this.port + "/user", "foo"); - } - - @Test - public void sunnyDay() { - assertThat(this.services.loadAuthentication("FOO").getName()).isEqualTo("me"); - } - - @Test - public void withRestTemplate() { - OAuth2ProtectedResourceDetails resource = new AuthorizationCodeResourceDetails(); - OAuth2ClientContext context = new DefaultOAuth2ClientContext(); - DefaultOAuth2AccessToken token = new DefaultOAuth2AccessToken("FOO"); - token.setRefreshToken(new DefaultExpiringOAuth2RefreshToken("BAR", new Date(0L))); - context.setAccessToken(token); - this.services.setRestTemplate(new OAuth2RestTemplate(resource, context)); - assertThat(this.services.loadAuthentication("FOO").getName()).isEqualTo("me"); - assertThat(context.getAccessToken().getValue()).isEqualTo("FOO"); - // The refresh token is still intact - assertThat(context.getAccessToken().getRefreshToken()) - .isEqualTo(token.getRefreshToken()); - } - - @Test - public void withRestTemplateChangesState() { - OAuth2ProtectedResourceDetails resource = new AuthorizationCodeResourceDetails(); - OAuth2ClientContext context = new DefaultOAuth2ClientContext(); - context.setAccessToken(new DefaultOAuth2AccessToken("FOO")); - this.services.setRestTemplate(new OAuth2RestTemplate(resource, context)); - assertThat(this.services.loadAuthentication("BAR").getName()).isEqualTo("me"); - assertThat(context.getAccessToken().getValue()).isEqualTo("BAR"); - } - - @Configuration - @Import({ ServletWebServerFactoryAutoConfiguration.class, - DispatcherServletAutoConfiguration.class, WebMvcAutoConfiguration.class, - HttpMessageConvertersAutoConfiguration.class, - PropertyPlaceholderAutoConfiguration.class }) - - @RestController - protected static class Application { - - @RequestMapping("/user") - public User user(@RequestHeader("Authorization") String authorization) { - if (authorization.endsWith("EXPIRED")) { - throw new InvalidTokenException("Expired"); - } - return new User(); - } - - @ExceptionHandler(InvalidTokenException.class) - @ResponseStatus(HttpStatus.UNAUTHORIZED) - public void expired() { - } - - } - - public static class User { - - private String userid = "me"; - - public String getUserid() { - return this.userid; - } - - public void setUserid(String userid) { - this.userid = userid; - } - - } - -} 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 deleted file mode 100644 index 8f70e8f5b8..0000000000 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/resource/UserInfoTokenServicesTests.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright 2012-2017 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.boot.autoconfigure.security.oauth2.resource; - -import java.util.Collections; -import java.util.LinkedHashMap; -import java.util.Map; - -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExpectedException; - -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.security.oauth2.client.OAuth2ClientContext; -import org.springframework.security.oauth2.client.OAuth2RestOperations; -import org.springframework.security.oauth2.client.resource.BaseOAuth2ProtectedResourceDetails; -import org.springframework.security.oauth2.client.resource.UserRedirectRequiredException; -import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken; -import org.springframework.security.oauth2.common.exceptions.InvalidTokenException; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.mock; - -/** - * Tests for {@link UserInfoTokenServices}. - * - * @author Dave Syer - */ -public class UserInfoTokenServicesTests { - - @Rule - public ExpectedException expected = ExpectedException.none(); - - private UserInfoTokenServices services = new UserInfoTokenServices( - "http://example.com", "foo"); - - private BaseOAuth2ProtectedResourceDetails resource = new BaseOAuth2ProtectedResourceDetails(); - - private OAuth2RestOperations template = mock(OAuth2RestOperations.class); - - private Map map = new LinkedHashMap<>(); - - @Before - public void init() { - this.resource.setClientId("foo"); - given(this.template.getForEntity(any(String.class), eq(Map.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() { - this.services.setRestTemplate(this.template); - assertThat(this.services.loadAuthentication("FOO").getName()) - .isEqualTo("unknown"); - } - - @Test - public void badToken() { - this.services.setRestTemplate(this.template); - given(this.template.getForEntity(any(String.class), eq(Map.class))) - .willThrow(new UserRedirectRequiredException("foo:bar", - Collections.emptyMap())); - this.expected.expect(InvalidTokenException.class); - assertThat(this.services.loadAuthentication("FOO").getName()) - .isEqualTo("unknown"); - } - - @Test - public void userId() { - this.map.put("userid", "spencer"); - this.services.setRestTemplate(this.template); - assertThat(this.services.loadAuthentication("FOO").getName()) - .isEqualTo("spencer"); - } - -} 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 deleted file mode 100644 index e608ad8be4..0000000000 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/sso/BasicOAuth2SsoConfigurationTests.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright 2012-2016 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.boot.autoconfigure.security.oauth2.sso; - -import javax.servlet.Filter; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.boot.autoconfigure.security.oauth2.OAuth2AutoConfiguration; -import org.springframework.boot.autoconfigure.security.oauth2.client.EnableOAuth2Sso; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Import; -import org.springframework.test.annotation.DirtiesContext; -import org.springframework.test.context.TestPropertySource; -import org.springframework.test.context.junit4.SpringRunner; -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; - -/** - * Tests for {@link OAuth2AutoConfiguration} with basic configuration. - * - * @author Dave Syer - */ -@RunWith(SpringRunner.class) -@DirtiesContext -@SpringBootTest -@TestPropertySource(properties = { "security.oauth2.client.clientId=client", - "security.oauth2.client.clientSecret=secret", - "security.oauth2.client.userAuthorizationUri=http://example.com/oauth/authorize", - "security.oauth2.client.accessTokenUri=http://example.com/oauth/token", - "security.oauth2.resource.jwt.keyValue=SSSSHHH" }) -public class BasicOAuth2SsoConfigurationTests { - - @Autowired - private WebApplicationContext context; - - @Autowired - @Qualifier("springSecurityFilterChain") - private Filter filter; - - private MockMvc mvc; - - @Before - public void init() { - this.mvc = MockMvcBuilders.webAppContextSetup(this.context) - .addFilters(this.filter).build(); - } - - @Test - public void homePageIsSecure() throws Exception { - this.mvc.perform(get("/")).andExpect(status().isFound()) - .andExpect(header().string("location", "http://localhost/login")); - } - - @Test - public void homePageSends401ToXhr() throws Exception { - this.mvc.perform(get("/").header("X-Requested-With", "XMLHttpRequest")) - .andExpect(status().isUnauthorized()); - } - - @Configuration - @Import(OAuth2AutoConfiguration.class) - @EnableOAuth2Sso - @MinimalSecureWebConfiguration - protected static class TestConfiguration { - - } - -} 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 deleted file mode 100644 index d1cf5464f1..0000000000 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/sso/CustomOAuth2SsoConfigurationTests.java +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Copyright 2012-2017 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.boot.autoconfigure.security.oauth2.sso; - -import javax.servlet.Filter; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.boot.autoconfigure.security.oauth2.OAuth2AutoConfiguration; -import org.springframework.boot.autoconfigure.security.oauth2.client.EnableOAuth2Sso; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Import; -import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; -import org.springframework.test.annotation.DirtiesContext; -import org.springframework.test.context.TestPropertySource; -import org.springframework.test.context.junit4.SpringRunner; -import org.springframework.test.web.servlet.MockMvc; -import org.springframework.test.web.servlet.setup.MockMvcBuilders; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; -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.content; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; - -/** - * Tests for {@link OAuth2AutoConfiguration} with custom configuration. - * - * @author Dave Syer - */ -@RunWith(SpringRunner.class) -@DirtiesContext -@SpringBootTest -@TestPropertySource(properties = { "security.oauth2.client.clientId=client", - "security.oauth2.client.clientSecret=secret", - "security.oauth2.client.authorizationUri=http://example.com/oauth/authorize", - "security.oauth2.client.tokenUri=http://example.com/oauth/token", - "security.oauth2.resource.jwt.keyValue=SSSSHHH" }) -public class CustomOAuth2SsoConfigurationTests { - - @Autowired - private WebApplicationContext context; - - @Autowired - @Qualifier("springSecurityFilterChain") - private Filter filter; - - private MockMvc mvc; - - @Before - public void init() { - this.mvc = MockMvcBuilders.webAppContextSetup(this.context) - .addFilters(this.filter).build(); - } - - @Test - public void uiPageIsSecure() throws Exception { - this.mvc.perform(get("/ui/")).andExpect(status().isFound()) - .andExpect(header().string("location", "http://localhost/login")); - } - - @Test - public void uiPageSends401ToXhr() throws Exception { - this.mvc.perform(get("/ui/").header("X-Requested-With", "XMLHttpRequest")) - .andExpect(status().isUnauthorized()); - } - - @Test - public void uiTestPageIsAccessible() throws Exception { - this.mvc.perform(get("/ui/test")).andExpect(status().isOk()) - .andExpect(content().string("test")); - } - - @Configuration - @EnableOAuth2Sso - @Import(OAuth2AutoConfiguration.class) - @MinimalSecureWebConfiguration - protected static class TestConfiguration extends WebSecurityConfigurerAdapter { - - @Override - public void configure(HttpSecurity http) throws Exception { - http.antMatcher("/ui/**").authorizeRequests().antMatchers("/ui/test") - .permitAll().anyRequest().authenticated(); - } - - @RestController - public static class TestController { - - @RequestMapping("/ui/test") - public String test() { - return "test"; - } - - } - - } - -} diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/sso/CustomOAuth2SsoWithAuthenticationEntryPointConfigurationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/sso/CustomOAuth2SsoWithAuthenticationEntryPointConfigurationTests.java deleted file mode 100644 index cc2080fd29..0000000000 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/sso/CustomOAuth2SsoWithAuthenticationEntryPointConfigurationTests.java +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright 2012-2017 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.boot.autoconfigure.security.oauth2.sso; - -import javax.servlet.Filter; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.boot.autoconfigure.security.oauth2.OAuth2AutoConfiguration; -import org.springframework.boot.autoconfigure.security.oauth2.client.EnableOAuth2Sso; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Import; -import org.springframework.http.HttpStatus; -import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; -import org.springframework.security.web.authentication.HttpStatusEntryPoint; -import org.springframework.test.annotation.DirtiesContext; -import org.springframework.test.context.TestPropertySource; -import org.springframework.test.context.junit4.SpringRunner; -import org.springframework.test.web.servlet.MockMvc; -import org.springframework.test.web.servlet.setup.MockMvcBuilders; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; -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.content; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; - -/** - * Tests for {@link OAuth2AutoConfiguration} with custom configuration. - * - * @author Dave Syer - */ -@RunWith(SpringRunner.class) -@DirtiesContext -@SpringBootTest -@TestPropertySource(properties = { "security.oauth2.client.clientId=client", - "security.oauth2.client.clientSecret=secret", - "security.oauth2.client.authorizationUri=http://example.com/oauth/authorize", - "security.oauth2.client.tokenUri=http://example.com/oauth/token", - "security.oauth2.resource.jwt.keyValue=SSSSHHH" }) -public class CustomOAuth2SsoWithAuthenticationEntryPointConfigurationTests { - - @Autowired - private WebApplicationContext context; - - @Autowired - @Qualifier("springSecurityFilterChain") - private Filter filter; - - private MockMvc mvc; - - @Before - public void init() { - this.mvc = MockMvcBuilders.webAppContextSetup(this.context) - .addFilters(this.filter).build(); - } - - @Test - public void uiPageIsSecure() throws Exception { - this.mvc.perform(get("/ui/")).andExpect(status().isUnauthorized()); - } - - @Test - public void uiTestPageIsAccessible() throws Exception { - this.mvc.perform(get("/ui/test")).andExpect(status().isOk()) - .andExpect(content().string("test")); - } - - @Configuration - @EnableOAuth2Sso - @Import(OAuth2AutoConfiguration.class) - @MinimalSecureWebConfiguration - protected static class TestConfiguration extends WebSecurityConfigurerAdapter { - - @Override - public void configure(HttpSecurity http) throws Exception { - http.antMatcher("/ui/**").authorizeRequests().antMatchers("/ui/test") - .permitAll().anyRequest().authenticated().and().exceptionHandling() - .authenticationEntryPoint( - new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED)); - } - - @RestController - public static class TestController { - - @RequestMapping("/ui/test") - public String test() { - return "test"; - } - - } - - } - -} diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/sso/CustomRestTemplateBasicOAuth2SsoConfigurationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/sso/CustomRestTemplateBasicOAuth2SsoConfigurationTests.java deleted file mode 100644 index 63a8ad3150..0000000000 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/sso/CustomRestTemplateBasicOAuth2SsoConfigurationTests.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright 2012-2016 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.boot.autoconfigure.security.oauth2.sso; - -import org.junit.Test; -import org.junit.runner.RunWith; - -import org.springframework.beans.factory.ObjectProvider; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.autoconfigure.security.oauth2.OAuth2AutoConfiguration; -import org.springframework.boot.autoconfigure.security.oauth2.client.EnableOAuth2Sso; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.context.ApplicationContext; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Import; -import org.springframework.context.annotation.Primary; -import org.springframework.test.annotation.DirtiesContext; -import org.springframework.test.context.TestPropertySource; -import org.springframework.test.context.junit4.SpringRunner; -import org.springframework.web.client.RestTemplate; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verifyZeroInteractions; - -/** - * Test to validate that a custom {@link RestTemplate} can be defined with OAuth2 SSO. - * - * @author Stephane Nicoll - */ -@RunWith(SpringRunner.class) -@DirtiesContext -@SpringBootTest -@TestPropertySource(properties = { "security.oauth2.client.clientId=client", - "security.oauth2.client.clientSecret=secret", - "security.oauth2.client.userAuthorizationUri=http://example.com/oauth/authorize", - "security.oauth2.client.accessTokenUri=http://example.com/oauth/token", - "security.oauth2.resource.jwt.keyValue=SSSSHHH" }) -public class CustomRestTemplateBasicOAuth2SsoConfigurationTests { - - @Autowired - private ApplicationContext applicationContext; - - @Autowired - private ObjectProvider restTemplate; - - @Test - public void customRestTemplateCanBePrimary() { - RestTemplate restTemplate = this.restTemplate.getIfAvailable(); - verifyZeroInteractions(restTemplate); - assertThat(this.applicationContext.getBeansOfType(RestTemplate.class)).hasSize(1); - } - - @Configuration - @Import(OAuth2AutoConfiguration.class) - @EnableOAuth2Sso - @MinimalSecureWebConfiguration - protected static class TestConfiguration { - - @Bean - @Primary - public RestTemplate myRestTemplate() { - return mock(RestTemplate.class); - } - - } - -} 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 deleted file mode 100644 index 38d5cde06c..0000000000 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/sso/MinimalSecureWebConfiguration.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2012-2017 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.boot.autoconfigure.security.oauth2.sso; - -import java.lang.annotation.Documented; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration; -import org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration; -import org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration; -import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration; -import org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration; -import org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration; -import org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Import; - -@Configuration -@Target(ElementType.TYPE) -@Retention(RetentionPolicy.RUNTIME) -@Documented -@Import({ ServletWebServerFactoryAutoConfiguration.class, - DispatcherServletAutoConfiguration.class, WebMvcAutoConfiguration.class, - HttpMessageConvertersAutoConfiguration.class, ErrorMvcAutoConfiguration.class, - PropertyPlaceholderAutoConfiguration.class, SecurityAutoConfiguration.class }) -public @interface MinimalSecureWebConfiguration { - -} diff --git a/spring-boot-cli/samples/oauth2.groovy b/spring-boot-cli/samples/oauth2.groovy deleted file mode 100644 index b9a5ce4bf5..0000000000 --- a/spring-boot-cli/samples/oauth2.groovy +++ /dev/null @@ -1,13 +0,0 @@ -package org.test - -@EnableAuthorizationServer -@EnableResourceServer -@RestController -class SampleController { - - @RequestMapping("/") - def hello() { - [message: "Hello World!"] - } - -} 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 deleted file mode 100644 index 3b3483a54f..0000000000 --- a/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/autoconfigure/SpringSecurityOAuth2CompilerAutoConfiguration.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * 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. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.boot.cli.compiler.autoconfigure; - -import org.codehaus.groovy.ast.ClassNode; -import org.codehaus.groovy.control.CompilationFailedException; -import org.codehaus.groovy.control.customizers.ImportCustomizer; - -import org.springframework.boot.cli.compiler.AstUtils; -import org.springframework.boot.cli.compiler.CompilerAutoConfiguration; -import org.springframework.boot.cli.compiler.DependencyCustomizer; - -/** - * {@link CompilerAutoConfiguration} for Spring Security OAuth2. - * - * @author Greg Turnquist - * @author Dave Syer - * @since 1.3.0 - */ -public class SpringSecurityOAuth2CompilerAutoConfiguration - extends CompilerAutoConfiguration { - - @Override - public boolean matches(ClassNode classNode) { - return AstUtils.hasAtLeastOneAnnotation(classNode, "EnableAuthorizationServer", - "EnableResourceServer", "EnableOAuth2Client", "EnableOAuth2Sso"); - } - - @Override - 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"); - imports.addStarImports( - "org.springframework.security.oauth2.config.annotation.web.configuration", - "org.springframework.security.access.prepost"); - } - -} diff --git a/spring-boot-cli/src/main/resources/META-INF/services/org.springframework.boot.cli.compiler.CompilerAutoConfiguration b/spring-boot-cli/src/main/resources/META-INF/services/org.springframework.boot.cli.compiler.CompilerAutoConfiguration index 3e699c3b05..20ff0c6126 100644 --- a/spring-boot-cli/src/main/resources/META-INF/services/org.springframework.boot.cli.compiler.CompilerAutoConfiguration +++ b/spring-boot-cli/src/main/resources/META-INF/services/org.springframework.boot.cli.compiler.CompilerAutoConfiguration @@ -8,7 +8,6 @@ org.springframework.boot.cli.compiler.autoconfigure.JdbcCompilerAutoConfiguratio org.springframework.boot.cli.compiler.autoconfigure.JmsCompilerAutoConfiguration org.springframework.boot.cli.compiler.autoconfigure.TransactionManagementCompilerAutoConfiguration org.springframework.boot.cli.compiler.autoconfigure.SpringIntegrationCompilerAutoConfiguration -org.springframework.boot.cli.compiler.autoconfigure.SpringSecurityOAuth2CompilerAutoConfiguration org.springframework.boot.cli.compiler.autoconfigure.SpringSecurityCompilerAutoConfiguration org.springframework.boot.cli.compiler.autoconfigure.SpringMobileCompilerAutoConfiguration org.springframework.boot.cli.compiler.autoconfigure.SpringRetryCompilerAutoConfiguration 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 2286c732bd..98faec912f 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 @@ -71,13 +71,6 @@ public class SampleIntegrationTests { assertThat(output).contains("completed with the following parameters"); } - @Test - public void oauth2Sample() throws Exception { - String output = this.cli.run("oauth2.groovy"); - assertThat(output).contains("security.oauth2.client.client-id"); - assertThat(output).contains("security.oauth2.client.client-secret ="); - } - @Test public void jobWebSample() throws Exception { String output = this.cli.run("job.groovy", "web.groovy", "foo=bar"); diff --git a/spring-boot-dependencies/pom.xml b/spring-boot-dependencies/pom.xml index a493e0b2c0..ec651a7e83 100644 --- a/spring-boot-dependencies/pom.xml +++ b/spring-boot-dependencies/pom.xml @@ -173,8 +173,6 @@ 1.2.1.RELEASE 1.2.1.RELEASE 5.0.0.M4 - 1.0.8.RELEASE - 2.2.0.RELEASE 2.0.0.M4 2.0.0.M4 3.0.0.M3 @@ -2358,21 +2356,6 @@ import pom - - org.springframework.security - spring-security-jwt - ${spring-security-jwt.version} - - - org.springframework.security.oauth - spring-security-oauth - ${spring-security-oauth.version} - - - org.springframework.security.oauth - spring-security-oauth2 - ${spring-security-oauth.version} - org.springframework.session spring-session-core diff --git a/spring-boot-docs/pom.xml b/spring-boot-docs/pom.xml index 0b02c0c1e7..5a0fc1a6a4 100644 --- a/spring-boot-docs/pom.xml +++ b/spring-boot-docs/pom.xml @@ -772,11 +772,6 @@ spring-security-web true - - org.springframework.security.oauth - spring-security-oauth2 - true - org.springframework.social spring-social-config diff --git a/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc b/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc index 3f5b891b53..4f250a1c42 100644 --- a/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc +++ b/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc @@ -476,27 +476,8 @@ content into your application; rather pick only the properties that you need. # SECURITY PROPERTIES # ---------------------------------------- # SECURITY ({sc-spring-boot-autoconfigure}/security/SecurityProperties.{sc-ext}[SecurityProperties]) - spring.security.filter.order=0 # Security filter chain order. - spring.security.filter.dispatcher-types=ASYNC,ERROR,REQUEST # Security filter chain dispatcher types. - - # SECURITY OAUTH2 CLIENT ({sc-spring-boot-autoconfigure}/security/oauth2/OAuth2ClientProperties.{sc-ext}[OAuth2ClientProperties]) - security.oauth2.client.client-id= # OAuth2 client id. - security.oauth2.client.client-secret= # OAuth2 client secret. A random secret is generated by default - - # SECURITY OAUTH2 RESOURCES ({sc-spring-boot-autoconfigure}/security/oauth2/resource/ResourceServerProperties.{sc-ext}[ResourceServerProperties]) - security.oauth2.resource.id= # Identifier of the resource. - security.oauth2.resource.jwt.key-uri= # The URI of the JWT token. Can be set if the value is not available and the key is public. - security.oauth2.resource.jwt.key-value= # The verification key of the JWT token. Can either be a symmetric secret or PEM-encoded RSA public key. - security.oauth2.resource.jwk.key-set-uri= # The URI for getting the set of keys that can be used to validate the token. - security.oauth2.resource.prefer-token-info=true # Use the token info, can be set to false to use the user info. - security.oauth2.resource.service-id=resource # - security.oauth2.resource.token-info-uri= # URI of the token decoding endpoint. - security.oauth2.resource.token-type= # The token type to send when using the userInfoUri. - security.oauth2.resource.user-info-uri= # URI of the user endpoint. - - # SECURITY OAUTH2 SSO ({sc-spring-boot-autoconfigure}/security/oauth2/client/OAuth2SsoProperties.{sc-ext}[OAuth2SsoProperties]) - security.oauth2.sso.login-path=/login # Path to the login page, i.e. the one that triggers the redirect to the OAuth2 Authorization Server - + spring.security.filter.order=0 # Security filter chain order. + spring.security.filter.dispatcher-types=ASYNC,ERROR,REQUEST # Security filter chain dispatcher types. # ---------------------------------------- # DATA PROPERTIES diff --git a/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc b/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc index dfc31a8b08..513c969307 100644 --- a/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc +++ b/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc @@ -2752,238 +2752,6 @@ explicitly configure the paths that you do want to override. -[[boot-features-security-oauth2]] -=== OAuth2 -If you have `spring-security-oauth2` on your classpath you can take advantage of some -auto-configuration to make it easy to set up Authorization or Resource Server. For full -details, see the {spring-security-oauth2-reference}[Spring Security OAuth 2 Developers -Guide]. - - - -[[boot-features-security-oauth2-authorization-server]] -==== Authorization Server -To create an Authorization Server and grant access tokens you need to use -`@EnableAuthorizationServer` and provide `security.oauth2.client.client-id` and -`security.oauth2.client.client-secret]` properties. The client will be registered for you -in an in-memory repository. - -Having done that you will be able to use the client credentials to create an access token, -for example: - -[indent=0] ----- - $ curl client:secret@localhost:8080/oauth/token -d grant_type=password -d username=user -d password=pwd ----- - -The basic auth credentials for the `/token` endpoint are the `client-id` and -`client-secret`. The user credentials are the normal Spring Security user details (which -default in Spring Boot to "`user`" and a random password). - -To switch off the auto-configuration and configure the Authorization Server features -yourself just add a `@Bean` of type `AuthorizationServerConfigurer`. - - - -[[boot-features-security-oauth2-resource-server]] -==== Resource Server -To use the access token you need a Resource Server (which can be the same as the -Authorization Server). Creating a Resource Server is easy, just add -`@EnableResourceServer` and provide some configuration to allow the server to decode -access tokens. If your application is also an Authorization Server it already knows how -to decode tokens, so there is nothing else to do. If your app is a standalone service then you -need to give it some more configuration, one of the following options: - -* `security.oauth2.resource.user-info-uri` to use the `/me` resource (e.g. -`\https://uaa.run.pivotal.io/userinfo` on Pivotal Web Services (PWS)) - -* `security.oauth2.resource.token-info-uri` to use the token decoding endpoint (e.g. -`\https://uaa.run.pivotal.io/check_token` on PWS). - -If you specify both the `user-info-uri` and the `token-info-uri` then you can set a flag -to say that one is preferred over the other (`prefer-token-info=true` is the default). - -Alternatively (instead of `user-info-uri` or `token-info-uri`) if the tokens are JWTs you -can configure a `security.oauth2.resource.jwt.key-value` to decode them locally (where the -key is a verification key). The verification key value is either a symmetric secret or -PEM-encoded RSA public key. If you don't have the key and it's public you can provide a -URI where it can be downloaded (as a JSON object with a "`value`" field) with -`security.oauth2.resource.jwt.key-uri`. E.g. on PWS: - -[indent=0] ----- - $ curl https://uaa.run.pivotal.io/token_key - {"alg":"SHA256withRSA","value":"-----BEGIN PUBLIC KEY-----\nMIIBI...\n-----END PUBLIC KEY-----\n"} ----- - -Additionally, if your authorization server has an endpoint that returns a set of JSON Web Keys(JWKs), -you can configure `security.oauth2.resource.jwk.key-set-uri`. E.g. on PWS: - -[indent=0] ----- - $ curl https://uaa.run.pivotal.io/token_keys - {"keys":[{"kid":"key-1","alg":"RS256","value":"-----BEGIN PUBLIC KEY-----\nMIIBI...\n-----END PUBLIC KEY-----\n"]} ----- - -NOTE: Configuring both JWT and JWK properties will cause an error. Only one of `security.oauth2.resource.jwt.key-uri` -(or `security.oauth2.resource.jwt.key-value`) and `security.oauth2.resource.jwk.key-set-uri` should be configured. - -WARNING: If you use the `security.oauth2.resource.jwt.key-uri` or `security.oauth2.resource.jwk.key-set-uri`, -the authorization server needs to be running when your application starts up. It will log a warning if it can't -find the key, and tell you what to do to fix it. - -OAuth2 resources are protected by a filter chain with order -`security.oauth2.resource.filter-order` and the default is after the filter protecting the -actuator endpoints by default (so actuator endpoints will stay on HTTP Basic unless you -change the order). - - - -[[boot-features-security-oauth2-token-type]] -=== Token Type in User Info -Google, and certain other 3rd party identity providers, are more strict about the token -type name that is sent in the headers to the user info endpoint. The default is "`Bearer`" -which suits most providers and matches the spec, but if you need to change it you can set -`security.oauth2.resource.token-type`. - - - -[[boot-features-security-custom-user-info]] -=== Customizing the User Info RestTemplate -If you have a `user-info-uri`, the resource server features use an `OAuth2RestTemplate` -internally to fetch user details for authentication. This is provided as a `@Bean` of -type `UserInfoRestTemplateFactory`. The default should be fine for most providers, but -occasionally you might need to add additional interceptors, or change the request -authenticator (which is how the token gets attached to outgoing requests). To add a -customization just create a bean of type `UserInfoRestTemplateCustomizer` - it has a -single method that will be called after the bean is created but before it is initialized. -The rest template that is being customized here is _only_ used internally to carry out -authentication. Alternatively, you could define your own `UserInfoRestTemplateFactory` -`@Bean` to take full control. - -[TIP] -==== -To set an RSA key value in YAML use the "`pipe`" continuation marker to split it over -multiple lines ("`|`") and remember to indent the key value (it's a standard YAML -language feature). Example: - -[source,yaml,indent=0] ----- - security: - oauth2: - resource: - jwt: - keyValue: | - -----BEGIN PUBLIC KEY----- - MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC... - -----END PUBLIC KEY----- ----- -==== - - - -[[boot-features-security-custom-user-info-client]] -==== Client -To make your web-app into an OAuth2 client you can simply add `@EnableOAuth2Client` and -Spring Boot will create an `OAuth2ClientContext` and `OAuth2ProtectedResourceDetails` that -are necessary to create an `OAuth2RestOperations`. Spring Boot does not automatically -create such bean but you can easily create your own: - -[source,java,indent=0] ----- - - @Bean - public OAuth2RestTemplate oauth2RestTemplate(OAuth2ClientContext oauth2ClientContext, - OAuth2ProtectedResourceDetails details) { - return new OAuth2RestTemplate(details, oauth2ClientContext); - } ----- - -NOTE: You may want to add a qualifier and review your configuration as more than one -`RestTemplate` may be defined in your application. - -This configuration uses `security.oauth2.client.*` as credentials (the same as you might -be using in the Authorization Server), but in addition it will need to know the -authorization and token URIs in the Authorization Server. For example: - -.application.yml -[source,yaml,indent=0] ----- - security: - oauth2: - client: - clientId: bd1c0a783ccdd1c9b9e4 - clientSecret: 1a9030fbca47a5b2c28e92f19050bb77824b5ad1 - accessTokenUri: https://github.com/login/oauth/access_token - userAuthorizationUri: https://github.com/login/oauth/authorize - clientAuthenticationScheme: form ----- - -An application with this configuration will redirect to Github for authorization when you -attempt to use the `OAuth2RestTemplate`. If you are already signed into Github you won't -even notice that it has authenticated. These specific credentials will only work if your -application is running on port 8080 (register your own client app in Github or other -provider for more flexibility). - -To limit the scope that the client asks for when it obtains an access token you can set -`security.oauth2.client.scope` (comma separated or an array in YAML). By default the scope -is empty and it is up to Authorization Server to decide what the defaults should be, -usually depending on the settings in the client registration that it holds. - -NOTE: There is also a setting for `security.oauth2.client.client-authentication-scheme` -which defaults to "`header`" (but you might need to set it to "`form`" if, like Github for -instance, your OAuth2 provider doesn't like header authentication). In fact, the -`security.oauth2.client.*` properties are bound to an instance of -`AuthorizationCodeResourceDetails` so all its properties can be specified. - -TIP: In a non-web application you can still create an `OAuth2RestOperations` and it -is still wired into the `security.oauth2.client.*` configuration. In this case it is a -"`client credentials token grant`" you will be asking for if you use it (and there is no -need to use `@EnableOAuth2Client` or `@EnableOAuth2Sso`). To prevent that infrastructure -to be defined, just remove the `security.oauth2.client.client-id` from your configuration -(or make it the empty string). - - - -[[boot-features-security-oauth2-single-sign-on]] -==== Single Sign On -An OAuth2 Client can be used to fetch user details from the provider (if such features are -available) and then convert them into an `Authentication` token for Spring Security. -The Resource Server above support this via the `user-info-uri` property This is the basis -for a Single Sign On (SSO) protocol based on OAuth2, and Spring Boot makes it easy to -participate by providing an annotation `@EnableOAuth2Sso`. The Github client above can -protect all its resources and authenticate using the Github `/user/` endpoint, by adding -that annotation and declaring where to find the endpoint (in addition to the -`security.oauth2.client.*` configuration already listed above): - -.application.yml -[source,yaml,indent=0]] ----- - security: - oauth2: - ... - resource: - userInfoUri: https://api.github.com/user - preferTokenInfo: false ----- - -Since all paths are secure by default, there is no "`home`" page that you can show to -unauthenticated users and invite them to login (by visiting the `/login` path, or the -path specified by `security.oauth2.sso.login-path`). - -To customize the access rules or paths to protect, so you can add a "`home`" page for -instance, `@EnableOAuth2Sso` can be added to a `WebSecurityConfigurerAdapter` and the -annotation will cause it to be decorated and enhanced with the necessary pieces to get -the `/login` path working. For example, here we simply allow unauthenticated access -to the home page at "/" and keep the default for everything else: - -[source,java,indent=0] ----- -include::{code-examples}/web/security/UnauthenticatedAccessExample.java[tag=configuration] ----- - - - [[boot-features-security-actuator]] === Actuator Security If the Actuator is also in use, you will find: diff --git a/spring-boot-samples/README.adoc b/spring-boot-samples/README.adoc index 92c5c4d3f3..ab6b03a734 100644 --- a/spring-boot-samples/README.adoc +++ b/spring-boot-samples/README.adoc @@ -146,15 +146,6 @@ The following sample applications are provided: | link:spring-boot-sample-secure[spring-boot-sample-secure] | Non-web application that uses Spring Security -| link:spring-boot-sample-secure-oauth2-actuator[spring-boot-sample-secure-oauth2-actuator] -| RESTful service secured using OAuth2 and Actuator - -| link:spring-boot-sample-secure-oauth2[spring-boot-sample-secure-oauth2] -| RESTful service secured using OAuth2 - -| link:spring-boot-sample-secure-oauth2-resource[spring-boot-sample-secure-oauth2-resource] -| OAuth2 resource server - | link:spring-boot-sample-servlet[spring-boot-sample-servlet] | Web application with a "raw" `Servlet` returning plain text content @@ -215,9 +206,6 @@ The following sample applications are provided: | link:spring-boot-sample-web-secure-custom[spring-boot-sample-web-secure-custom] | Web application with custom Spring Security configuration -| link:spring-boot-sample-web-secure-github[spring-boot-sample-web-secure-github] -| Web application with Spring Security configured to authenticate with GitHub using OAuth2 - | link:spring-boot-sample-web-secure-jdbc[spring-boot-sample-web-secure-jdbc] | Web application with Spring Security configured to use JDBC authentication diff --git a/spring-boot-samples/pom.xml b/spring-boot-samples/pom.xml index 366ad6e29e..4c652f926e 100644 --- a/spring-boot-samples/pom.xml +++ b/spring-boot-samples/pom.xml @@ -67,9 +67,6 @@ spring-boot-sample-property-validation spring-boot-sample-quartz spring-boot-sample-secure - spring-boot-sample-secure-oauth2 - spring-boot-sample-secure-oauth2-actuator - spring-boot-sample-secure-oauth2-resource spring-boot-sample-servlet spring-boot-sample-session spring-boot-sample-simple @@ -91,7 +88,6 @@ spring-boot-sample-web-mustache spring-boot-sample-web-secure spring-boot-sample-web-secure-custom - spring-boot-sample-web-secure-github spring-boot-sample-web-secure-jdbc spring-boot-sample-web-static spring-boot-sample-web-ui diff --git a/spring-boot-samples/spring-boot-sample-secure-oauth2-actuator/pom.xml b/spring-boot-samples/spring-boot-sample-secure-oauth2-actuator/pom.xml deleted file mode 100644 index 821a672664..0000000000 --- a/spring-boot-samples/spring-boot-sample-secure-oauth2-actuator/pom.xml +++ /dev/null @@ -1,54 +0,0 @@ - - - 4.0.0 - - - org.springframework.boot - spring-boot-samples - 2.0.0.BUILD-SNAPSHOT - - spring-boot-sample-secure-oauth2-actuator - spring-boot-sample-secure-oauth2-actuator - Spring Boot Security OAuth2 Actuator Sample - http://projects.spring.io/spring-boot/ - - Pivotal Software, Inc. - http://www.spring.io - - - ${basedir}/../.. - - - - - org.springframework.boot - spring-boot-starter-security - - - org.springframework.boot - spring-boot-starter-web - - - org.springframework.boot - spring-boot-starter-actuator - - - org.springframework.security.oauth - spring-security-oauth2 - - - - org.springframework.boot - spring-boot-starter-test - test - - - - - - org.springframework.boot - spring-boot-maven-plugin - - - - diff --git a/spring-boot-samples/spring-boot-sample-secure-oauth2-actuator/src/main/java/sample/secure/oauth2/actuator/ActuatorSecurityConfiguration.java b/spring-boot-samples/spring-boot-sample-secure-oauth2-actuator/src/main/java/sample/secure/oauth2/actuator/ActuatorSecurityConfiguration.java deleted file mode 100644 index d2354d49ce..0000000000 --- a/spring-boot-samples/spring-boot-sample-secure-oauth2-actuator/src/main/java/sample/secure/oauth2/actuator/ActuatorSecurityConfiguration.java +++ /dev/null @@ -1,28 +0,0 @@ -package sample.secure.oauth2.actuator; - -import org.springframework.boot.actuate.autoconfigure.security.EndpointRequest; -import org.springframework.context.annotation.Configuration; -import org.springframework.core.annotation.Order; -import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; - -/** - * Basic auth security for actuator endpoints. - * - * @author Madhura Bhave - */ -@Configuration -@Order(2) // before the resource server configuration -public class ActuatorSecurityConfiguration extends WebSecurityConfigurerAdapter { - - @Override - protected void configure(HttpSecurity http) throws Exception { - // @formatter:off - http.requestMatcher(EndpointRequest.toAnyEndpoint()).authorizeRequests() - .antMatchers("/**").authenticated() - .and() - .httpBasic(); - // @formatter:on - } - -} diff --git a/spring-boot-samples/spring-boot-sample-secure-oauth2-actuator/src/main/java/sample/secure/oauth2/actuator/SampleSecureOAuth2ActuatorApplication.java b/spring-boot-samples/spring-boot-sample-secure-oauth2-actuator/src/main/java/sample/secure/oauth2/actuator/SampleSecureOAuth2ActuatorApplication.java deleted file mode 100644 index 0f9d8199ed..0000000000 --- a/spring-boot-samples/spring-boot-sample-secure-oauth2-actuator/src/main/java/sample/secure/oauth2/actuator/SampleSecureOAuth2ActuatorApplication.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright 2012-2017 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package sample.secure.oauth2.actuator; - -import java.util.UUID; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.context.annotation.Bean; -import org.springframework.security.core.userdetails.User; -import org.springframework.security.core.userdetails.UserDetailsService; -import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer; -import org.springframework.security.provisioning.InMemoryUserDetailsManager; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RestController; - -@SpringBootApplication -@EnableResourceServer -@RestController -public class SampleSecureOAuth2ActuatorApplication { - - @GetMapping("/") - public Message home() { - return new Message("Hello World"); - } - - @Bean - public UserDetailsService userDetailsService() throws Exception { - InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager(); - manager.createUser( - User.withUsername("user").password("password").roles("USER").build()); - return manager; - } - - public static void main(String[] args) { - SpringApplication.run(SampleSecureOAuth2ActuatorApplication.class, args); - } - - class Message { - - private String id = UUID.randomUUID().toString(); - - private String value; - - public Message(String value) { - this.value = value; - } - - public String getId() { - return this.id; - } - - public String getValue() { - return this.value; - } - - } - -} diff --git a/spring-boot-samples/spring-boot-sample-secure-oauth2-actuator/src/main/resources/application.properties b/spring-boot-samples/spring-boot-sample-secure-oauth2-actuator/src/main/resources/application.properties deleted file mode 100644 index a4b588c279..0000000000 --- a/spring-boot-samples/spring-boot-sample-secure-oauth2-actuator/src/main/resources/application.properties +++ /dev/null @@ -1,5 +0,0 @@ -server.port=8081 -endpoints.default.web.enabled=true -security.oauth2.resource.id=service -security.oauth2.resource.userInfoUri=http://localhost:8080/user -logging.level.org.springframework.security=DEBUG diff --git a/spring-boot-samples/spring-boot-sample-secure-oauth2-actuator/src/test/java/sample/secure/oauth2/actuator/SampleSecureOAuth2ActuatorApplicationTests.java b/spring-boot-samples/spring-boot-sample-secure-oauth2-actuator/src/test/java/sample/secure/oauth2/actuator/SampleSecureOAuth2ActuatorApplicationTests.java deleted file mode 100644 index 8e56db2277..0000000000 --- a/spring-boot-samples/spring-boot-sample-secure-oauth2-actuator/src/test/java/sample/secure/oauth2/actuator/SampleSecureOAuth2ActuatorApplicationTests.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright 2012-2017 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package sample.secure.oauth2.actuator; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.security.web.FilterChainProxy; -import org.springframework.test.context.junit4.SpringRunner; -import org.springframework.test.web.servlet.MockMvc; -import org.springframework.util.Base64Utils; -import org.springframework.web.context.WebApplicationContext; - -import static org.hamcrest.CoreMatchers.containsString; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; -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 - * - * @author Dave Syer - */ -@RunWith(SpringRunner.class) -@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT) -public class SampleSecureOAuth2ActuatorApplicationTests { - - @Autowired - private WebApplicationContext context; - - @Autowired - private FilterChainProxy filterChain; - - private MockMvc mvc; - - @Before - public void setUp() { - this.mvc = webAppContextSetup(this.context).addFilters(this.filterChain).build(); - SecurityContextHolder.clearContext(); - } - - @Test - public void homePageSecuredByDefault() throws Exception { - this.mvc.perform(get("/")).andExpect(status().isUnauthorized()) - .andExpect(header().string("WWW-Authenticate", containsString("Bearer"))) - .andDo(print()); - } - - @Test - public void healthSecured() throws Exception { - this.mvc.perform(get("/application/health")).andExpect(status().isUnauthorized()); - } - - @Test - public void healthWithBasicAuthorization() throws Exception { - this.mvc.perform(get("/application/health").header("Authorization", - "Basic " + Base64Utils.encodeToString("user:password".getBytes()))) - .andExpect(status().isOk()); - } - - @Test - public void envSecured() throws Exception { - this.mvc.perform(get("/application/env")).andExpect(status().isUnauthorized()); - } - - @Test - public void envWithBasicAuthorization() throws Exception { - this.mvc.perform(get("/application/env").header("Authorization", - "Basic " + Base64Utils.encodeToString("user:password".getBytes()))) - .andExpect(status().isOk()).andDo(print()); - } - -} diff --git a/spring-boot-samples/spring-boot-sample-secure-oauth2-resource/pom.xml b/spring-boot-samples/spring-boot-sample-secure-oauth2-resource/pom.xml deleted file mode 100644 index daffe70107..0000000000 --- a/spring-boot-samples/spring-boot-sample-secure-oauth2-resource/pom.xml +++ /dev/null @@ -1,59 +0,0 @@ - - - 4.0.0 - - - org.springframework.boot - spring-boot-samples - 2.0.0.BUILD-SNAPSHOT - - spring-boot-sample-secure-oauth2-resource - spring-boot-sample-secure-oauth2-resource - Spring Boot Security OAuth2 Sample - http://projects.spring.io/spring-boot/ - - Pivotal Software, Inc. - http://www.spring.io - - - ${basedir}/../.. - - - - - org.springframework.boot - spring-boot-starter-security - - - org.springframework.boot - spring-boot-starter-data-jpa - - - org.springframework.boot - spring-boot-starter-data-rest - - - com.h2database - h2 - - - org.springframework.security.oauth - spring-security-oauth2 - - - - org.springframework.boot - spring-boot-starter-test - test - - - - - - org.springframework.boot - spring-boot-maven-plugin - - - - diff --git a/spring-boot-samples/spring-boot-sample-secure-oauth2-resource/src/main/java/sample/secure/oauth2/resource/Flight.java b/spring-boot-samples/spring-boot-sample-secure-oauth2-resource/src/main/java/sample/secure/oauth2/resource/Flight.java deleted file mode 100644 index 3f2c24449b..0000000000 --- a/spring-boot-samples/spring-boot-sample-secure-oauth2-resource/src/main/java/sample/secure/oauth2/resource/Flight.java +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright 2012-2017 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package sample.secure.oauth2.resource; - -import java.util.Date; - -import javax.persistence.Entity; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; - -/** - * Domain object for tracking flights - * - * @author Craig Walls - * @author Greg Turnquist - */ -@Entity -@JsonIgnoreProperties(ignoreUnknown = true) -public class Flight { - - @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 this.id; - } - - public void setId(Long id) { - this.id = id; - } - - public String getOrigin() { - return this.origin; - } - - public void setOrigin(String origin) { - this.origin = origin; - } - - public String getDestination() { - return this.destination; - } - - public void setDestination(String destination) { - this.destination = destination; - } - - public String getAirline() { - return this.airline; - } - - public void setAirline(String airline) { - this.airline = airline; - } - - public String getFlightNumber() { - return this.flightNumber; - } - - public void setFlightNumber(String flightNumber) { - this.flightNumber = flightNumber; - } - - public Date getDate() { - return this.date; - } - - public void setDate(Date date) { - this.date = date; - } - - public String getTraveler() { - return this.traveler; - } - - public void setTraveler(String traveler) { - this.traveler = traveler; - } - -} diff --git a/spring-boot-samples/spring-boot-sample-secure-oauth2-resource/src/main/java/sample/secure/oauth2/resource/FlightRepository.java b/spring-boot-samples/spring-boot-sample-secure-oauth2-resource/src/main/java/sample/secure/oauth2/resource/FlightRepository.java deleted file mode 100644 index 07d2fea4a4..0000000000 --- a/spring-boot-samples/spring-boot-sample-secure-oauth2-resource/src/main/java/sample/secure/oauth2/resource/FlightRepository.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2012-2017 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package sample.secure.oauth2.resource; - -import java.util.Optional; - -import org.springframework.data.repository.CrudRepository; - -/** - * Spring Data interface with secured methods - * - * @author Craig Walls - * @author Greg Turnquist - */ -public interface FlightRepository extends CrudRepository { - - @Override - Iterable findAll(); - - @Override - Optional findById(Long aLong); - - @Override - S save(S entity); - -} diff --git a/spring-boot-samples/spring-boot-sample-secure-oauth2-resource/src/main/java/sample/secure/oauth2/resource/SampleSecureOAuth2ResourceApplication.java b/spring-boot-samples/spring-boot-sample-secure-oauth2-resource/src/main/java/sample/secure/oauth2/resource/SampleSecureOAuth2ResourceApplication.java deleted file mode 100644 index 95a79a81f7..0000000000 --- a/spring-boot-samples/spring-boot-sample-secure-oauth2-resource/src/main/java/sample/secure/oauth2/resource/SampleSecureOAuth2ResourceApplication.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2012-2017 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package sample.secure.oauth2.resource; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer; -import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter; - -@SpringBootApplication -@EnableResourceServer -public class SampleSecureOAuth2ResourceApplication - extends ResourceServerConfigurerAdapter { - - @Override - public void configure(HttpSecurity http) throws Exception { - http.antMatcher("/flights/**").authorizeRequests().anyRequest().authenticated(); - } - - public static void main(String[] args) { - SpringApplication.run(SampleSecureOAuth2ResourceApplication.class, args); - } - -} diff --git a/spring-boot-samples/spring-boot-sample-secure-oauth2-resource/src/main/resources/application.properties b/spring-boot-samples/spring-boot-sample-secure-oauth2-resource/src/main/resources/application.properties deleted file mode 100644 index ce2ab022b2..0000000000 --- a/spring-boot-samples/spring-boot-sample-secure-oauth2-resource/src/main/resources/application.properties +++ /dev/null @@ -1,5 +0,0 @@ -server.port=8081 -spring.datasource.platform=h2 -security.oauth2.resource.id=service -security.oauth2.resource.userInfoUri=http://localhost:8080/user -logging.level.org.springframework.security=DEBUG diff --git a/spring-boot-samples/spring-boot-sample-secure-oauth2-resource/src/main/resources/data-h2.sql b/spring-boot-samples/spring-boot-sample-secure-oauth2-resource/src/main/resources/data-h2.sql deleted file mode 100644 index 478a2ebf91..0000000000 --- a/spring-boot-samples/spring-boot-sample-secure-oauth2-resource/src/main/resources/data-h2.sql +++ /dev/null @@ -1,4 +0,0 @@ -insert into FLIGHT -(id, origin, destination, airline, flight_number, traveler) -values -(1, 'Nashville', 'Dallas', 'Spring Ways', 'OAUTH2', 'Greg Turnquist'); diff --git a/spring-boot-samples/spring-boot-sample-secure-oauth2-resource/src/test/java/sample/secure/oauth2/resource/SampleSecureOAuth2ResourceApplicationTests.java b/spring-boot-samples/spring-boot-sample-secure-oauth2-resource/src/test/java/sample/secure/oauth2/resource/SampleSecureOAuth2ResourceApplicationTests.java deleted file mode 100644 index 964a2814a7..0000000000 --- a/spring-boot-samples/spring-boot-sample-secure-oauth2-resource/src/test/java/sample/secure/oauth2/resource/SampleSecureOAuth2ResourceApplicationTests.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright 2012-2017 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package sample.secure.oauth2.resource; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; -import org.springframework.hateoas.MediaTypes; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.security.web.FilterChainProxy; -import org.springframework.test.context.junit4.SpringRunner; -import org.springframework.test.web.servlet.MockMvc; -import org.springframework.web.context.WebApplicationContext; - -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; -import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; -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 - * - * @author Greg Turnquist - * @author Dave Syer - */ -@RunWith(SpringRunner.class) -@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT) -public class SampleSecureOAuth2ResourceApplicationTests { - - @Autowired - private WebApplicationContext context; - - @Autowired - private FilterChainProxy filterChain; - - private MockMvc mvc; - - @Before - public void setUp() { - this.mvc = webAppContextSetup(this.context).addFilters(this.filterChain).build(); - SecurityContextHolder.clearContext(); - } - - @Test - public void homePageAvailable() throws Exception { - this.mvc.perform(get("/").accept(MediaTypes.HAL_JSON)).andExpect(status().isOk()) - .andDo(print()); - } - - @Test - public void flightsSecuredByDefault() throws Exception { - 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()); - } - - @Test - public void profileAvailable() throws Exception { - this.mvc.perform(get("/profile").accept(MediaTypes.HAL_JSON)) - .andExpect(status().isOk()).andDo(print()); - } - -} diff --git a/spring-boot-samples/spring-boot-sample-secure-oauth2/pom.xml b/spring-boot-samples/spring-boot-sample-secure-oauth2/pom.xml deleted file mode 100644 index 9297d63a1d..0000000000 --- a/spring-boot-samples/spring-boot-sample-secure-oauth2/pom.xml +++ /dev/null @@ -1,58 +0,0 @@ - - - 4.0.0 - - - org.springframework.boot - spring-boot-samples - 2.0.0.BUILD-SNAPSHOT - - spring-boot-sample-secure-oauth2 - Spring Boot Security OAuth2 Sample - Spring Boot Security OAuth2 Sample - http://projects.spring.io/spring-boot/ - - Pivotal Software, Inc. - http://www.spring.io - - - ${basedir}/../.. - - - - - org.springframework.boot - spring-boot-starter-security - - - org.springframework.boot - spring-boot-starter-data-jpa - - - org.springframework.boot - spring-boot-starter-data-rest - - - com.h2database - h2 - - - org.springframework.security.oauth - spring-security-oauth2 - - - - org.springframework.boot - spring-boot-starter-test - test - - - - - - org.springframework.boot - spring-boot-maven-plugin - - - - diff --git a/spring-boot-samples/spring-boot-sample-secure-oauth2/src/main/java/sample/secure/oauth2/AuthenticationConfiguration.java b/spring-boot-samples/spring-boot-sample-secure-oauth2/src/main/java/sample/secure/oauth2/AuthenticationConfiguration.java deleted file mode 100644 index a2bca3dc86..0000000000 --- a/spring-boot-samples/spring-boot-sample-secure-oauth2/src/main/java/sample/secure/oauth2/AuthenticationConfiguration.java +++ /dev/null @@ -1,18 +0,0 @@ -package sample.secure.oauth2; - -import org.springframework.context.annotation.Configuration; -import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; -import org.springframework.security.config.annotation.authentication.configurers.GlobalAuthenticationConfigurerAdapter; - -/** - * @author Madhura Bhave - */ -@Configuration -public class AuthenticationConfiguration extends GlobalAuthenticationConfigurerAdapter { - - @Override - public void init(AuthenticationManagerBuilder auth) throws Exception { - auth.inMemoryAuthentication().withUser("greg").password("turnquist") - .roles("read"); - } -} diff --git a/spring-boot-samples/spring-boot-sample-secure-oauth2/src/main/java/sample/secure/oauth2/Flight.java b/spring-boot-samples/spring-boot-sample-secure-oauth2/src/main/java/sample/secure/oauth2/Flight.java deleted file mode 100644 index f83eb6f2ef..0000000000 --- a/spring-boot-samples/spring-boot-sample-secure-oauth2/src/main/java/sample/secure/oauth2/Flight.java +++ /dev/null @@ -1,110 +0,0 @@ -/* - * 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. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package sample.secure.oauth2; - -import java.util.Date; - -import javax.persistence.Entity; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; - -/** - * Domain object for tracking flights - * - * @author Craig Walls - * @author Greg Turnquist - */ -@Entity -@JsonIgnoreProperties(ignoreUnknown = true) -public class Flight { - - @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 this.id; - } - - public void setId(Long id) { - this.id = id; - } - - public String getOrigin() { - return this.origin; - } - - public void setOrigin(String origin) { - this.origin = origin; - } - - public String getDestination() { - return this.destination; - } - - public void setDestination(String destination) { - this.destination = destination; - } - - public String getAirline() { - return this.airline; - } - - public void setAirline(String airline) { - this.airline = airline; - } - - public String getFlightNumber() { - return this.flightNumber; - } - - public void setFlightNumber(String flightNumber) { - this.flightNumber = flightNumber; - } - - public Date getDate() { - return this.date; - } - - public void setDate(Date date) { - this.date = date; - } - - public String getTraveler() { - 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/secure/oauth2/FlightRepository.java b/spring-boot-samples/spring-boot-sample-secure-oauth2/src/main/java/sample/secure/oauth2/FlightRepository.java deleted file mode 100644 index 6f2da766ba..0000000000 --- a/spring-boot-samples/spring-boot-sample-secure-oauth2/src/main/java/sample/secure/oauth2/FlightRepository.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright 2012-2017 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package sample.secure.oauth2; - -import java.util.Optional; - -import org.springframework.data.repository.CrudRepository; -import org.springframework.security.access.prepost.PreAuthorize; - -/** - * Spring Data interface with secured methods - * - * @author Craig Walls - * @author Greg Turnquist - */ -public interface FlightRepository extends CrudRepository { - - @Override - @PreAuthorize("#oauth2.hasScope('read')") - Iterable findAll(); - - @Override - @PreAuthorize("#oauth2.hasScope('read')") - Optional findById(Long aLong); - - @Override - @PreAuthorize("#oauth2.hasScope('write')") - S save(S entity); - -} diff --git a/spring-boot-samples/spring-boot-sample-secure-oauth2/src/main/java/sample/secure/oauth2/SampleSecureOAuth2Application.java b/spring-boot-samples/spring-boot-sample-secure-oauth2/src/main/java/sample/secure/oauth2/SampleSecureOAuth2Application.java deleted file mode 100644 index c894a09143..0000000000 --- a/spring-boot-samples/spring-boot-sample-secure-oauth2/src/main/java/sample/secure/oauth2/SampleSecureOAuth2Application.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright 2012-2016 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package sample.secure.oauth2; - -import java.security.Principal; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; -import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer; -import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RestController; - -/** - * 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
- * 
- * - *
    - *
  • grant_type=password (user credentials will be supplied)
  • - *
  • scope=read (read only scope)
  • - *
  • username=greg (username checked against user details service)
  • - *
  • password=turnquist (password checked against user details service)
  • - *
  • -u foo:bar (clientid:secret)
  • - *
- * - * Response should be similar to this: - * {"access_token":"533de99b-5a0f-4175-8afd-1a64feb952d5","token_type":"bearer","expires_in":43199,"scope":"read"} - * - * With the token value, you can now interrogate the RESTful interface like this: - * - *
- * curl -H "Authorization: bearer [access_token]" localhost:8080/flights/1
- * 
- * - * You should then see the pre-loaded data like this: - * - *
- * {
- *      "origin" : "Nashville",
- *      "destination" : "Dallas",
- *      "airline" : "Spring Ways",
- *      "flightNumber" : "OAUTH2",
- *      "date" : null,
- *      "traveler" : "Greg Turnquist",
- *      "_links" : {
- *          "self" : {
- *              "href" : "http://localhost:8080/flights/1"
- *          }
- *      }
- * }
- * 
- * - * Test creating a new entry: - * - *
- * curl -i -H "Authorization: bearer [access token]" -H "Content-Type:application/json" localhost:8080/flights -X POST -d @flight.json
- * 
- * - * Insufficient scope? (read not write) Ask for a new token! - * - *
- * curl localhost:8080/oauth/token -d "grant_type=password&scope=write&username=greg&password=turnquist" -u foo:bar
- *
- * {"access_token":"cfa69736-e2aa-4ae7-abbb-3085acda560e","token_type":"bearer","expires_in":43200,"scope":"write"}
- * 
- * - * Retry with the new token. There should be a Location header. - * - *
- * Location: http://localhost:8080/flights/2
- *
- * curl -H "Authorization: bearer [access token]" localhost:8080/flights/2
- * 
- * - * @author Craig Walls - * @author Greg Turnquist - */ -@SpringBootApplication -@EnableAuthorizationServer -@EnableResourceServer -@EnableGlobalMethodSecurity(prePostEnabled = true) -@RestController -public class SampleSecureOAuth2Application { - - @GetMapping("/user") - public Principal user(Principal user) { - return user; - } - - public static void main(String[] args) { - SpringApplication.run(SampleSecureOAuth2Application.class, args); - } - -} diff --git a/spring-boot-samples/spring-boot-sample-secure-oauth2/src/main/resources/application.properties b/spring-boot-samples/spring-boot-sample-secure-oauth2/src/main/resources/application.properties deleted file mode 100644 index 517152df98..0000000000 --- a/spring-boot-samples/spring-boot-sample-secure-oauth2/src/main/resources/application.properties +++ /dev/null @@ -1,7 +0,0 @@ -spring.datasource.platform=h2 - -security.oauth2.client.client-id=foo -security.oauth2.client.client-secret=bar -security.oauth2.authorization.checkTokenAccess=isAuthenticated() - -logging.level.org.springframework.security=DEBUG diff --git a/spring-boot-samples/spring-boot-sample-secure-oauth2/src/main/resources/data-h2.sql b/spring-boot-samples/spring-boot-sample-secure-oauth2/src/main/resources/data-h2.sql deleted file mode 100644 index 478a2ebf91..0000000000 --- a/spring-boot-samples/spring-boot-sample-secure-oauth2/src/main/resources/data-h2.sql +++ /dev/null @@ -1,4 +0,0 @@ -insert into FLIGHT -(id, origin, destination, airline, flight_number, traveler) -values -(1, 'Nashville', 'Dallas', 'Spring Ways', 'OAUTH2', 'Greg Turnquist'); diff --git a/spring-boot-samples/spring-boot-sample-secure-oauth2/src/test/java/sample/secure/oauth2/SampleSecureOAuth2ApplicationTests.java b/spring-boot-samples/spring-boot-sample-secure-oauth2/src/test/java/sample/secure/oauth2/SampleSecureOAuth2ApplicationTests.java deleted file mode 100644 index e4abe4d285..0000000000 --- a/spring-boot-samples/spring-boot-sample-secure-oauth2/src/test/java/sample/secure/oauth2/SampleSecureOAuth2ApplicationTests.java +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Copyright 2012-2017 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package sample.secure.oauth2; - -import java.util.Base64; -import java.util.Map; - -import com.fasterxml.jackson.databind.ObjectMapper; -import org.junit.Before; -import org.junit.Ignore; -import org.junit.Test; -import org.junit.runner.RunWith; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; -import org.springframework.hateoas.MediaTypes; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.security.web.FilterChainProxy; -import org.springframework.test.context.junit4.SpringRunner; -import org.springframework.test.web.servlet.MockMvc; -import org.springframework.test.web.servlet.MvcResult; -import org.springframework.web.context.WebApplicationContext; - -import static org.assertj.core.api.Assertions.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 - * - * @author Greg Turnquist - */ -@RunWith(SpringRunner.class) -@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT) -public class SampleSecureOAuth2ApplicationTests { - - @Autowired - private WebApplicationContext context; - - @Autowired - private FilterChainProxy filterChain; - - private MockMvc mvc; - - private final ObjectMapper objectMapper = new ObjectMapper(); - - @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()); - } - - @Test - @Ignore - public void accessingRootUriPossibleWithUserAccount() throws Exception { - String header = "Basic " - + new String(Base64.getEncoder().encode("greg:turnquist".getBytes())); - this.mvc.perform( - 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 { - String header = "Basic " - + new String(Base64.getEncoder().encode("foo:bar".getBytes())); - MvcResult result = this.mvc - .perform(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() + ";charset=UTF-8")) - .andExpect(status().isOk()).andDo(print()).andReturn(); - - Flight flight = this.objectMapper.readValue( - flightsAction.getResponse().getContentAsString(), Flight.class); - - assertThat(flight.getOrigin()).isEqualTo("Nashville"); - assertThat(flight.getDestination()).isEqualTo("Dallas"); - assertThat(flight.getAirline()).isEqualTo("Spring Ways"); - assertThat(flight.getFlightNumber()).isEqualTo("OAUTH2"); - assertThat(flight.getTraveler()).isEqualTo("Greg Turnquist"); - } - -} diff --git a/spring-boot-samples/spring-boot-sample-web-secure-github/pom.xml b/spring-boot-samples/spring-boot-sample-web-secure-github/pom.xml deleted file mode 100644 index 8a81296a45..0000000000 --- a/spring-boot-samples/spring-boot-sample-web-secure-github/pom.xml +++ /dev/null @@ -1,55 +0,0 @@ - - - 4.0.0 - - - org.springframework.boot - spring-boot-samples - 2.0.0.BUILD-SNAPSHOT - - spring-boot-sample-web-secure-github - Spring Boot Web Secure GitHub Sample - Spring Boot Web Secure GitHub Sample - http://projects.spring.io/spring-boot/ - - Pivotal Software, Inc. - http://www.spring.io - - - ${basedir}/../.. - - - - - org.springframework.boot - spring-boot-starter-security - - - org.springframework.boot - spring-boot-starter-web - - - org.springframework.security.oauth - spring-security-oauth2 - - - - org.apache.httpcomponents - httpclient - test - - - org.springframework.boot - spring-boot-starter-test - test - - - - - - org.springframework.boot - spring-boot-maven-plugin - - - - diff --git a/spring-boot-samples/spring-boot-sample-web-secure-github/src/main/java/sample/web/secure/github/SampleGithubSecureApplication.java b/spring-boot-samples/spring-boot-sample-web-secure-github/src/main/java/sample/web/secure/github/SampleGithubSecureApplication.java deleted file mode 100644 index 3df427d0c6..0000000000 --- a/spring-boot-samples/spring-boot-sample-web-secure-github/src/main/java/sample/web/secure/github/SampleGithubSecureApplication.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2012-2017 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package sample.web.secure.github; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.boot.autoconfigure.security.oauth2.client.EnableOAuth2Sso; -import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; - -@SpringBootApplication -@EnableOAuth2Sso -public class SampleGithubSecureApplication implements WebMvcConfigurer { - - public static void main(String[] args) throws Exception { - SpringApplication.run(SampleGithubSecureApplication.class, args); - } - -} diff --git a/spring-boot-samples/spring-boot-sample-web-secure-github/src/main/resources/application.yml b/spring-boot-samples/spring-boot-sample-web-secure-github/src/main/resources/application.yml deleted file mode 100644 index ddf1305b25..0000000000 --- a/spring-boot-samples/spring-boot-sample-web-secure-github/src/main/resources/application.yml +++ /dev/null @@ -1,11 +0,0 @@ -security: - oauth2: - client: - clientId: bd1c0a783ccdd1c9b9e4 - clientSecret: 1a9030fbca47a5b2c28e92f19050bb77824b5ad1 - accessTokenUri: https://github.com/login/oauth/access_token - userAuthorizationUri: https://github.com/login/oauth/authorize - clientAuthenticationScheme: form - resource: - userInfoUri: https://api.github.com/user - preferTokenInfo: false diff --git a/spring-boot-samples/spring-boot-sample-web-secure-github/src/main/resources/static/css/bootstrap.min.css b/spring-boot-samples/spring-boot-sample-web-secure-github/src/main/resources/static/css/bootstrap.min.css deleted file mode 100644 index 5589964e71..0000000000 --- a/spring-boot-samples/spring-boot-sample-web-secure-github/src/main/resources/static/css/bootstrap.min.css +++ /dev/null @@ -1,11 +0,0 @@ -/*! - * Bootstrap v2.0.4 - * - * Copyright 2012 Twitter, Inc - * Licensed under the Apache License v2.0 - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Designed and built with all the love in the world @twitter by @mdo and @fat. - */article,aside,details,figcaption,figure,footer,header,hgroup,nav,section{display:block}audio,canvas,video{display:inline-block;*display:inline;*zoom:1}audio:not([controls]){display:none}html{font-size:100%;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}a:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}a:hover,a:active{outline:0}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}img{max-width:100%;vertical-align:middle;border:0;-ms-interpolation-mode:bicubic}#map_canvas img{max-width:none}button,input,select,textarea{margin:0;font-size:100%;vertical-align:middle}button,input{*overflow:visible;line-height:normal}button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0}button,input[type="button"],input[type="reset"],input[type="submit"]{cursor:pointer;-webkit-appearance:button}input[type="search"]{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;-webkit-appearance:textfield}input[type="search"]::-webkit-search-decoration,input[type="search"]::-webkit-search-cancel-button{-webkit-appearance:none}textarea{overflow:auto;vertical-align:top}.clearfix{*zoom:1}.clearfix:before,.clearfix:after{display:table;content:""}.clearfix:after{clear:both}.hide-text{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.input-block-level{display:block;width:100%;min-height:28px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;-ms-box-sizing:border-box;box-sizing:border-box}body{margin:0;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:13px;line-height:18px;color:#333;background-color:#fff}a{color:#08c;text-decoration:none}a:hover{color:#005580;text-decoration:underline}.row{margin-left:-20px;*zoom:1}.row:before,.row:after{display:table;content:""}.row:after{clear:both}[class*="span"]{float:left;margin-left:20px}.container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:940px}.span12{width:940px}.span11{width:860px}.span10{width:780px}.span9{width:700px}.span8{width:620px}.span7{width:540px}.span6{width:460px}.span5{width:380px}.span4{width:300px}.span3{width:220px}.span2{width:140px}.span1{width:60px}.offset12{margin-left:980px}.offset11{margin-left:900px}.offset10{margin-left:820px}.offset9{margin-left:740px}.offset8{margin-left:660px}.offset7{margin-left:580px}.offset6{margin-left:500px}.offset5{margin-left:420px}.offset4{margin-left:340px}.offset3{margin-left:260px}.offset2{margin-left:180px}.offset1{margin-left:100px}.row-fluid{width:100%;*zoom:1}.row-fluid:before,.row-fluid:after{display:table;content:""}.row-fluid:after{clear:both}.row-fluid [class*="span"]{display:block;float:left;width:100%;min-height:28px;margin-left:2.127659574%;*margin-left:2.0744680846382977%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;-ms-box-sizing:border-box;box-sizing:border-box}.row-fluid [class*="span"]:first-child{margin-left:0}.row-fluid .span12{width:99.99999998999999%;*width:99.94680850063828%}.row-fluid .span11{width:91.489361693%;*width:91.4361702036383%}.row-fluid .span10{width:82.97872339599999%;*width:82.92553190663828%}.row-fluid .span9{width:74.468085099%;*width:74.4148936096383%}.row-fluid .span8{width:65.95744680199999%;*width:65.90425531263828%}.row-fluid .span7{width:57.446808505%;*width:57.3936170156383%}.row-fluid .span6{width:48.93617020799999%;*width:48.88297871863829%}.row-fluid .span5{width:40.425531911%;*width:40.3723404216383%}.row-fluid .span4{width:31.914893614%;*width:31.8617021246383%}.row-fluid .span3{width:23.404255317%;*width:23.3510638276383%}.row-fluid .span2{width:14.89361702%;*width:14.8404255306383%}.row-fluid .span1{width:6.382978723%;*width:6.329787233638298%}.container{margin-right:auto;margin-left:auto;*zoom:1}.container:before,.container:after{display:table;content:""}.container:after{clear:both}.container-fluid{padding-right:20px;padding-left:20px;*zoom:1}.container-fluid:before,.container-fluid:after{display:table;content:""}.container-fluid:after{clear:both}p{margin:0 0 9px}p small{font-size:11px;color:#999}.lead{margin-bottom:18px;font-size:20px;font-weight:200;line-height:27px}h1,h2,h3,h4,h5,h6{margin:0;font-family:inherit;font-weight:bold;color:inherit;text-rendering:optimizelegibility}h1 small,h2 small,h3 small,h4 small,h5 small,h6 small{font-weight:normal;color:#999}h1{font-size:30px;line-height:36px}h1 small{font-size:18px}h2{font-size:24px;line-height:36px}h2 small{font-size:18px}h3{font-size:18px;line-height:27px}h3 small{font-size:14px}h4,h5,h6{line-height:18px}h4{font-size:14px}h4 small{font-size:12px}h5{font-size:12px}h6{font-size:11px;color:#999;text-transform:uppercase}.page-header{padding-bottom:17px;margin:18px 0;border-bottom:1px solid #eee}.page-header h1{line-height:1}ul,ol{padding:0;margin:0 0 9px 25px}ul ul,ul ol,ol ol,ol ul{margin-bottom:0}ul{list-style:disc}ol{list-style:decimal}li{line-height:18px}ul.unstyled,ol.unstyled{margin-left:0;list-style:none}dl{margin-bottom:18px}dt,dd{line-height:18px}dt{font-weight:bold;line-height:17px}dd{margin-left:9px}.dl-horizontal dt{float:left;width:120px;overflow:hidden;clear:left;text-align:right;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:130px}hr{margin:18px 0;border:0;border-top:1px solid #eee;border-bottom:1px solid #fff}strong{font-weight:bold}em{font-style:italic}.muted{color:#999}abbr[title]{cursor:help;border-bottom:1px dotted #999}abbr.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:0 0 0 15px;margin:0 0 18px;border-left:5px solid #eee}blockquote p{margin-bottom:0;font-size:16px;font-weight:300;line-height:22.5px}blockquote small{display:block;line-height:18px;color:#999}blockquote small:before{content:'\2014 \00A0'}blockquote.pull-right{float:right;padding-right:15px;padding-left:0;border-right:5px solid #eee;border-left:0}blockquote.pull-right p,blockquote.pull-right small{text-align:right}q:before,q:after,blockquote:before,blockquote:after{content:""}address{display:block;margin-bottom:18px;font-style:normal;line-height:18px}small{font-size:100%}cite{font-style:normal}code,pre{padding:0 3px 2px;font-family:Menlo,Monaco,Consolas,"Courier New",monospace;font-size:12px;color:#333;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}code{padding:2px 4px;color:#d14;background-color:#f7f7f9;border:1px solid #e1e1e8}pre{display:block;padding:8.5px;margin:0 0 9px;font-size:12.025px;line-height:18px;word-break:break-all;word-wrap:break-word;white-space:pre;white-space:pre-wrap;background-color:#f5f5f5;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.15);-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}pre.prettyprint{margin-bottom:18px}pre code{padding:0;color:inherit;background-color:transparent;border:0}.pre-scrollable{max-height:340px;overflow-y:scroll}form{margin:0 0 18px}fieldset{padding:0;margin:0;border:0}legend{display:block;width:100%;padding:0;margin-bottom:27px;font-size:19.5px;line-height:36px;color:#333;border:0;border-bottom:1px solid #e5e5e5}legend small{font-size:13.5px;color:#999}label,input,button,select,textarea{font-size:13px;font-weight:normal;line-height:18px}input,button,select,textarea{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif}label{display:block;margin-bottom:5px}select,textarea,input[type="text"],input[type="password"],input[type="datetime"],input[type="datetime-local"],input[type="date"],input[type="month"],input[type="time"],input[type="week"],input[type="number"],input[type="email"],input[type="url"],input[type="search"],input[type="tel"],input[type="color"],.uneditable-input{display:inline-block;height:18px;padding:4px;margin-bottom:9px;font-size:13px;line-height:18px;color:#555}input,textarea{width:210px}textarea{height:auto}textarea,input[type="text"],input[type="password"],input[type="datetime"],input[type="datetime-local"],input[type="date"],input[type="month"],input[type="time"],input[type="week"],input[type="number"],input[type="email"],input[type="url"],input[type="search"],input[type="tel"],input[type="color"],.uneditable-input{background-color:#fff;border:1px solid #ccc;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-webkit-transition:border linear .2s,box-shadow linear .2s;-moz-transition:border linear .2s,box-shadow linear .2s;-ms-transition:border linear .2s,box-shadow linear .2s;-o-transition:border linear .2s,box-shadow linear .2s;transition:border linear .2s,box-shadow linear .2s}textarea:focus,input[type="text"]:focus,input[type="password"]:focus,input[type="datetime"]:focus,input[type="datetime-local"]:focus,input[type="date"]:focus,input[type="month"]:focus,input[type="time"]:focus,input[type="week"]:focus,input[type="number"]:focus,input[type="email"]:focus,input[type="url"]:focus,input[type="search"]:focus,input[type="tel"]:focus,input[type="color"]:focus,.uneditable-input:focus{border-color:rgba(82,168,236,0.8);outline:0;outline:thin dotted \9;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(82,168,236,0.6);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(82,168,236,0.6);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(82,168,236,0.6)}input[type="radio"],input[type="checkbox"]{margin:3px 0;*margin-top:0;line-height:normal;cursor:pointer}input[type="submit"],input[type="reset"],input[type="button"],input[type="radio"],input[type="checkbox"]{width:auto}.uneditable-textarea{width:auto;height:auto}select,input[type="file"]{height:28px;*margin-top:4px;line-height:28px}select{width:220px;border:1px solid #bbb}select[multiple],select[size]{height:auto}select:focus,input[type="file"]:focus,input[type="radio"]:focus,input[type="checkbox"]:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.radio,.checkbox{min-height:18px;padding-left:18px}.radio input[type="radio"],.checkbox input[type="checkbox"]{float:left;margin-left:-18px}.controls>.radio:first-child,.controls>.checkbox:first-child{padding-top:5px}.radio.inline,.checkbox.inline{display:inline-block;padding-top:5px;margin-bottom:0;vertical-align:middle}.radio.inline+.radio.inline,.checkbox.inline+.checkbox.inline{margin-left:10px}.input-mini{width:60px}.input-small{width:90px}.input-medium{width:150px}.input-large{width:210px}.input-xlarge{width:270px}.input-xxlarge{width:530px}input[class*="span"],select[class*="span"],textarea[class*="span"],.uneditable-input[class*="span"],.row-fluid input[class*="span"],.row-fluid select[class*="span"],.row-fluid textarea[class*="span"],.row-fluid .uneditable-input[class*="span"]{float:none;margin-left:0}.input-append input[class*="span"],.input-append .uneditable-input[class*="span"],.input-prepend input[class*="span"],.input-prepend .uneditable-input[class*="span"],.row-fluid .input-prepend [class*="span"],.row-fluid .input-append [class*="span"]{display:inline-block}input,textarea,.uneditable-input{margin-left:0}input.span12,textarea.span12,.uneditable-input.span12{width:930px}input.span11,textarea.span11,.uneditable-input.span11{width:850px}input.span10,textarea.span10,.uneditable-input.span10{width:770px}input.span9,textarea.span9,.uneditable-input.span9{width:690px}input.span8,textarea.span8,.uneditable-input.span8{width:610px}input.span7,textarea.span7,.uneditable-input.span7{width:530px}input.span6,textarea.span6,.uneditable-input.span6{width:450px}input.span5,textarea.span5,.uneditable-input.span5{width:370px}input.span4,textarea.span4,.uneditable-input.span4{width:290px}input.span3,textarea.span3,.uneditable-input.span3{width:210px}input.span2,textarea.span2,.uneditable-input.span2{width:130px}input.span1,textarea.span1,.uneditable-input.span1{width:50px}input[disabled],select[disabled],textarea[disabled],input[readonly],select[readonly],textarea[readonly]{cursor:not-allowed;background-color:#eee;border-color:#ddd}input[type="radio"][disabled],input[type="checkbox"][disabled],input[type="radio"][readonly],input[type="checkbox"][readonly]{background-color:transparent}.control-group.warning>label,.control-group.warning .help-block,.control-group.warning .help-inline{color:#c09853}.control-group.warning .checkbox,.control-group.warning .radio,.control-group.warning input,.control-group.warning select,.control-group.warning textarea{color:#c09853;border-color:#c09853}.control-group.warning .checkbox:focus,.control-group.warning .radio:focus,.control-group.warning input:focus,.control-group.warning select:focus,.control-group.warning textarea:focus{border-color:#a47e3c;-webkit-box-shadow:0 0 6px #dbc59e;-moz-box-shadow:0 0 6px #dbc59e;box-shadow:0 0 6px #dbc59e}.control-group.warning .input-prepend .add-on,.control-group.warning .input-append .add-on{color:#c09853;background-color:#fcf8e3;border-color:#c09853}.control-group.error>label,.control-group.error .help-block,.control-group.error .help-inline{color:#b94a48}.control-group.error .checkbox,.control-group.error .radio,.control-group.error input,.control-group.error select,.control-group.error textarea{color:#b94a48;border-color:#b94a48}.control-group.error .checkbox:focus,.control-group.error .radio:focus,.control-group.error input:focus,.control-group.error select:focus,.control-group.error textarea:focus{border-color:#953b39;-webkit-box-shadow:0 0 6px #d59392;-moz-box-shadow:0 0 6px #d59392;box-shadow:0 0 6px #d59392}.control-group.error .input-prepend .add-on,.control-group.error .input-append .add-on{color:#b94a48;background-color:#f2dede;border-color:#b94a48}.control-group.success>label,.control-group.success .help-block,.control-group.success .help-inline{color:#468847}.control-group.success .checkbox,.control-group.success .radio,.control-group.success input,.control-group.success select,.control-group.success textarea{color:#468847;border-color:#468847}.control-group.success .checkbox:focus,.control-group.success .radio:focus,.control-group.success input:focus,.control-group.success select:focus,.control-group.success textarea:focus{border-color:#356635;-webkit-box-shadow:0 0 6px #7aba7b;-moz-box-shadow:0 0 6px #7aba7b;box-shadow:0 0 6px #7aba7b}.control-group.success .input-prepend .add-on,.control-group.success .input-append .add-on{color:#468847;background-color:#dff0d8;border-color:#468847}input:focus:required:invalid,textarea:focus:required:invalid,select:focus:required:invalid{color:#b94a48;border-color:#ee5f5b}input:focus:required:invalid:focus,textarea:focus:required:invalid:focus,select:focus:required:invalid:focus{border-color:#e9322d;-webkit-box-shadow:0 0 6px #f8b9b7;-moz-box-shadow:0 0 6px #f8b9b7;box-shadow:0 0 6px #f8b9b7}.form-actions{padding:17px 20px 18px;margin-top:18px;margin-bottom:18px;background-color:#f5f5f5;border-top:1px solid #e5e5e5;*zoom:1}.form-actions:before,.form-actions:after{display:table;content:""}.form-actions:after{clear:both}.uneditable-input{overflow:hidden;white-space:nowrap;cursor:not-allowed;background-color:#fff;border-color:#eee;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,0.025);-moz-box-shadow:inset 0 1px 2px rgba(0,0,0,0.025);box-shadow:inset 0 1px 2px rgba(0,0,0,0.025)}:-moz-placeholder{color:#999}:-ms-input-placeholder{color:#999}::-webkit-input-placeholder{color:#999}.help-block,.help-inline{color:#555}.help-block{display:block;margin-bottom:9px}.help-inline{display:inline-block;*display:inline;padding-left:5px;vertical-align:middle;*zoom:1}.input-prepend,.input-append{margin-bottom:5px}.input-prepend input,.input-append input,.input-prepend select,.input-append select,.input-prepend .uneditable-input,.input-append .uneditable-input{position:relative;margin-bottom:0;*margin-left:0;vertical-align:middle;-webkit-border-radius:0 3px 3px 0;-moz-border-radius:0 3px 3px 0;border-radius:0 3px 3px 0}.input-prepend input:focus,.input-append input:focus,.input-prepend select:focus,.input-append select:focus,.input-prepend .uneditable-input:focus,.input-append .uneditable-input:focus{z-index:2}.input-prepend .uneditable-input,.input-append .uneditable-input{border-left-color:#ccc}.input-prepend .add-on,.input-append .add-on{display:inline-block;width:auto;height:18px;min-width:16px;padding:4px 5px;font-weight:normal;line-height:18px;text-align:center;text-shadow:0 1px 0 #fff;vertical-align:middle;background-color:#eee;border:1px solid #ccc}.input-prepend .add-on,.input-append .add-on,.input-prepend .btn,.input-append .btn{margin-left:-1px;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.input-prepend .active,.input-append .active{background-color:#a9dba9;border-color:#46a546}.input-prepend .add-on,.input-prepend .btn{margin-right:-1px}.input-prepend .add-on:first-child,.input-prepend .btn:first-child{-webkit-border-radius:3px 0 0 3px;-moz-border-radius:3px 0 0 3px;border-radius:3px 0 0 3px}.input-append input,.input-append select,.input-append .uneditable-input{-webkit-border-radius:3px 0 0 3px;-moz-border-radius:3px 0 0 3px;border-radius:3px 0 0 3px}.input-append .uneditable-input{border-right-color:#ccc;border-left-color:#eee}.input-append .add-on:last-child,.input-append .btn:last-child{-webkit-border-radius:0 3px 3px 0;-moz-border-radius:0 3px 3px 0;border-radius:0 3px 3px 0}.input-prepend.input-append input,.input-prepend.input-append select,.input-prepend.input-append .uneditable-input{-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.input-prepend.input-append .add-on:first-child,.input-prepend.input-append .btn:first-child{margin-right:-1px;-webkit-border-radius:3px 0 0 3px;-moz-border-radius:3px 0 0 3px;border-radius:3px 0 0 3px}.input-prepend.input-append .add-on:last-child,.input-prepend.input-append .btn:last-child{margin-left:-1px;-webkit-border-radius:0 3px 3px 0;-moz-border-radius:0 3px 3px 0;border-radius:0 3px 3px 0}.search-query{padding-right:14px;padding-right:4px \9;padding-left:14px;padding-left:4px \9;margin-bottom:0;-webkit-border-radius:14px;-moz-border-radius:14px;border-radius:14px}.form-search input,.form-inline input,.form-horizontal input,.form-search textarea,.form-inline textarea,.form-horizontal textarea,.form-search select,.form-inline select,.form-horizontal select,.form-search .help-inline,.form-inline .help-inline,.form-horizontal .help-inline,.form-search .uneditable-input,.form-inline .uneditable-input,.form-horizontal .uneditable-input,.form-search .input-prepend,.form-inline .input-prepend,.form-horizontal .input-prepend,.form-search .input-append,.form-inline .input-append,.form-horizontal .input-append{display:inline-block;*display:inline;margin-bottom:0;*zoom:1}.form-search .hide,.form-inline .hide,.form-horizontal .hide{display:none}.form-search label,.form-inline label{display:inline-block}.form-search .input-append,.form-inline .input-append,.form-search .input-prepend,.form-inline .input-prepend{margin-bottom:0}.form-search .radio,.form-search .checkbox,.form-inline .radio,.form-inline .checkbox{padding-left:0;margin-bottom:0;vertical-align:middle}.form-search .radio input[type="radio"],.form-search .checkbox input[type="checkbox"],.form-inline .radio input[type="radio"],.form-inline .checkbox input[type="checkbox"]{float:left;margin-right:3px;margin-left:0}.control-group{margin-bottom:9px}legend+.control-group{margin-top:18px;-webkit-margin-top-collapse:separate}.form-horizontal .control-group{margin-bottom:18px;*zoom:1}.form-horizontal .control-group:before,.form-horizontal .control-group:after{display:table;content:""}.form-horizontal .control-group:after{clear:both}.form-horizontal .control-label{float:left;width:140px;padding-top:5px;text-align:right}.form-horizontal .controls{*display:inline-block;*padding-left:20px;margin-left:160px;*margin-left:0}.form-horizontal .controls:first-child{*padding-left:160px}.form-horizontal .help-block{margin-top:9px;margin-bottom:0}.form-horizontal .form-actions{padding-left:160px}table{max-width:100%;background-color:transparent;border-collapse:collapse;border-spacing:0}.table{width:100%;margin-bottom:18px}.table th,.table td{padding:8px;line-height:18px;text-align:left;vertical-align:top;border-top:1px solid #ddd}.table th{font-weight:bold}.table thead th{vertical-align:bottom}.table caption+thead tr:first-child th,.table caption+thead tr:first-child td,.table colgroup+thead tr:first-child th,.table colgroup+thead tr:first-child td,.table thead:first-child tr:first-child th,.table thead:first-child tr:first-child td{border-top:0}.table tbody+tbody{border-top:2px solid #ddd}.table-condensed th,.table-condensed td{padding:4px 5px}.table-bordered{border:1px solid #ddd;border-collapse:separate;*border-collapse:collapsed;border-left:0;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.table-bordered th,.table-bordered td{border-left:1px solid #ddd}.table-bordered caption+thead tr:first-child th,.table-bordered caption+tbody tr:first-child th,.table-bordered caption+tbody tr:first-child td,.table-bordered colgroup+thead tr:first-child th,.table-bordered colgroup+tbody tr:first-child th,.table-bordered colgroup+tbody tr:first-child td,.table-bordered thead:first-child tr:first-child th,.table-bordered tbody:first-child tr:first-child th,.table-bordered tbody:first-child tr:first-child td{border-top:0}.table-bordered thead:first-child tr:first-child th:first-child,.table-bordered tbody:first-child tr:first-child td:first-child{-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-topleft:4px}.table-bordered thead:first-child tr:first-child th:last-child,.table-bordered tbody:first-child tr:first-child td:last-child{-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-moz-border-radius-topright:4px}.table-bordered thead:last-child tr:last-child th:first-child,.table-bordered tbody:last-child tr:last-child td:first-child{-webkit-border-radius:0 0 0 4px;-moz-border-radius:0 0 0 4px;border-radius:0 0 0 4px;-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px;-moz-border-radius-bottomleft:4px}.table-bordered thead:last-child tr:last-child th:last-child,.table-bordered tbody:last-child tr:last-child td:last-child{-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px;-moz-border-radius-bottomright:4px}.table-striped tbody tr:nth-child(odd) td,.table-striped tbody tr:nth-child(odd) th{background-color:#f9f9f9}.table tbody tr:hover td,.table tbody tr:hover th{background-color:#f5f5f5}table .span1{float:none;width:44px;margin-left:0}table .span2{float:none;width:124px;margin-left:0}table .span3{float:none;width:204px;margin-left:0}table .span4{float:none;width:284px;margin-left:0}table .span5{float:none;width:364px;margin-left:0}table .span6{float:none;width:444px;margin-left:0}table .span7{float:none;width:524px;margin-left:0}table .span8{float:none;width:604px;margin-left:0}table .span9{float:none;width:684px;margin-left:0}table .span10{float:none;width:764px;margin-left:0}table .span11{float:none;width:844px;margin-left:0}table .span12{float:none;width:924px;margin-left:0}table .span13{float:none;width:1004px;margin-left:0}table .span14{float:none;width:1084px;margin-left:0}table .span15{float:none;width:1164px;margin-left:0}table .span16{float:none;width:1244px;margin-left:0}table .span17{float:none;width:1324px;margin-left:0}table .span18{float:none;width:1404px;margin-left:0}table .span19{float:none;width:1484px;margin-left:0}table .span20{float:none;width:1564px;margin-left:0}table .span21{float:none;width:1644px;margin-left:0}table .span22{float:none;width:1724px;margin-left:0}table .span23{float:none;width:1804px;margin-left:0}table .span24{float:none;width:1884px;margin-left:0}[class^="icon-"],[class*=" icon-"]{display:inline-block;width:14px;height:14px;*margin-right:.3em;line-height:14px;vertical-align:text-top;background-image:url("../img/glyphicons-halflings.png");background-position:14px 14px;background-repeat:no-repeat}[class^="icon-"]:last-child,[class*=" icon-"]:last-child{*margin-left:0}.icon-white{background-image:url("../img/glyphicons-halflings-white.png")}.icon-glass{background-position:0 0}.icon-music{background-position:-24px 0}.icon-search{background-position:-48px 0}.icon-envelope{background-position:-72px 0}.icon-heart{background-position:-96px 0}.icon-star{background-position:-120px 0}.icon-star-empty{background-position:-144px 0}.icon-user{background-position:-168px 0}.icon-film{background-position:-192px 0}.icon-th-large{background-position:-216px 0}.icon-th{background-position:-240px 0}.icon-th-list{background-position:-264px 0}.icon-ok{background-position:-288px 0}.icon-remove{background-position:-312px 0}.icon-zoom-in{background-position:-336px 0}.icon-zoom-out{background-position:-360px 0}.icon-off{background-position:-384px 0}.icon-signal{background-position:-408px 0}.icon-cog{background-position:-432px 0}.icon-trash{background-position:-456px 0}.icon-home{background-position:0 -24px}.icon-file{background-position:-24px -24px}.icon-time{background-position:-48px -24px}.icon-road{background-position:-72px -24px}.icon-download-alt{background-position:-96px -24px}.icon-download{background-position:-120px -24px}.icon-upload{background-position:-144px -24px}.icon-inbox{background-position:-168px -24px}.icon-play-circle{background-position:-192px -24px}.icon-repeat{background-position:-216px -24px}.icon-refresh{background-position:-240px -24px}.icon-list-alt{background-position:-264px -24px}.icon-lock{background-position:-287px -24px}.icon-flag{background-position:-312px -24px}.icon-headphones{background-position:-336px -24px}.icon-volume-off{background-position:-360px -24px}.icon-volume-down{background-position:-384px -24px}.icon-volume-up{background-position:-408px -24px}.icon-qrcode{background-position:-432px -24px}.icon-barcode{background-position:-456px -24px}.icon-tag{background-position:0 -48px}.icon-tags{background-position:-25px -48px}.icon-book{background-position:-48px -48px}.icon-bookmark{background-position:-72px -48px}.icon-print{background-position:-96px -48px}.icon-camera{background-position:-120px -48px}.icon-font{background-position:-144px -48px}.icon-bold{background-position:-167px -48px}.icon-italic{background-position:-192px -48px}.icon-text-height{background-position:-216px -48px}.icon-text-width{background-position:-240px -48px}.icon-align-left{background-position:-264px -48px}.icon-align-center{background-position:-288px -48px}.icon-align-right{background-position:-312px -48px}.icon-align-justify{background-position:-336px -48px}.icon-list{background-position:-360px -48px}.icon-indent-left{background-position:-384px -48px}.icon-indent-right{background-position:-408px -48px}.icon-facetime-video{background-position:-432px -48px}.icon-picture{background-position:-456px -48px}.icon-pencil{background-position:0 -72px}.icon-map-marker{background-position:-24px -72px}.icon-adjust{background-position:-48px -72px}.icon-tint{background-position:-72px -72px}.icon-edit{background-position:-96px -72px}.icon-share{background-position:-120px -72px}.icon-check{background-position:-144px -72px}.icon-move{background-position:-168px -72px}.icon-step-backward{background-position:-192px -72px}.icon-fast-backward{background-position:-216px -72px}.icon-backward{background-position:-240px -72px}.icon-play{background-position:-264px -72px}.icon-pause{background-position:-288px -72px}.icon-stop{background-position:-312px -72px}.icon-forward{background-position:-336px -72px}.icon-fast-forward{background-position:-360px -72px}.icon-step-forward{background-position:-384px -72px}.icon-eject{background-position:-408px -72px}.icon-chevron-left{background-position:-432px -72px}.icon-chevron-right{background-position:-456px -72px}.icon-plus-sign{background-position:0 -96px}.icon-minus-sign{background-position:-24px -96px}.icon-remove-sign{background-position:-48px -96px}.icon-ok-sign{background-position:-72px -96px}.icon-question-sign{background-position:-96px -96px}.icon-info-sign{background-position:-120px -96px}.icon-screenshot{background-position:-144px -96px}.icon-remove-circle{background-position:-168px -96px}.icon-ok-circle{background-position:-192px -96px}.icon-ban-circle{background-position:-216px -96px}.icon-arrow-left{background-position:-240px -96px}.icon-arrow-right{background-position:-264px -96px}.icon-arrow-up{background-position:-289px -96px}.icon-arrow-down{background-position:-312px -96px}.icon-share-alt{background-position:-336px -96px}.icon-resize-full{background-position:-360px -96px}.icon-resize-small{background-position:-384px -96px}.icon-plus{background-position:-408px -96px}.icon-minus{background-position:-433px -96px}.icon-asterisk{background-position:-456px -96px}.icon-exclamation-sign{background-position:0 -120px}.icon-gift{background-position:-24px -120px}.icon-leaf{background-position:-48px -120px}.icon-fire{background-position:-72px -120px}.icon-eye-open{background-position:-96px -120px}.icon-eye-close{background-position:-120px -120px}.icon-warning-sign{background-position:-144px -120px}.icon-plane{background-position:-168px -120px}.icon-calendar{background-position:-192px -120px}.icon-random{background-position:-216px -120px}.icon-comment{background-position:-240px -120px}.icon-magnet{background-position:-264px -120px}.icon-chevron-up{background-position:-288px -120px}.icon-chevron-down{background-position:-313px -119px}.icon-retweet{background-position:-336px -120px}.icon-shopping-cart{background-position:-360px -120px}.icon-folder-close{background-position:-384px -120px}.icon-folder-open{background-position:-408px -120px}.icon-resize-vertical{background-position:-432px -119px}.icon-resize-horizontal{background-position:-456px -118px}.icon-hdd{background-position:0 -144px}.icon-bullhorn{background-position:-24px -144px}.icon-bell{background-position:-48px -144px}.icon-certificate{background-position:-72px -144px}.icon-thumbs-up{background-position:-96px -144px}.icon-thumbs-down{background-position:-120px -144px}.icon-hand-right{background-position:-144px -144px}.icon-hand-left{background-position:-168px -144px}.icon-hand-up{background-position:-192px -144px}.icon-hand-down{background-position:-216px -144px}.icon-circle-arrow-right{background-position:-240px -144px}.icon-circle-arrow-left{background-position:-264px -144px}.icon-circle-arrow-up{background-position:-288px -144px}.icon-circle-arrow-down{background-position:-312px -144px}.icon-globe{background-position:-336px -144px}.icon-wrench{background-position:-360px -144px}.icon-tasks{background-position:-384px -144px}.icon-filter{background-position:-408px -144px}.icon-briefcase{background-position:-432px -144px}.icon-fullscreen{background-position:-456px -144px}.dropup,.dropdown{position:relative}.dropdown-toggle{*margin-bottom:-3px}.dropdown-toggle:active,.open .dropdown-toggle{outline:0}.caret{display:inline-block;width:0;height:0;vertical-align:top;border-top:4px solid #000;border-right:4px solid transparent;border-left:4px solid transparent;content:"";opacity:.3;filter:alpha(opacity=30)}.dropdown .caret{margin-top:8px;margin-left:2px}.dropdown:hover .caret,.open .caret{opacity:1;filter:alpha(opacity=100)}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:4px 0;margin:1px 0 0;list-style:none;background-color:#fff;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.2);*border-right-width:2px;*border-bottom-width:2px;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,0.2);-moz-box-shadow:0 5px 10px rgba(0,0,0,0.2);box-shadow:0 5px 10px rgba(0,0,0,0.2);-webkit-background-clip:padding-box;-moz-background-clip:padding;background-clip:padding-box}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{*width:100%;height:1px;margin:8px 1px;*margin:-5px 0 5px;overflow:hidden;background-color:#e5e5e5;border-bottom:1px solid #fff}.dropdown-menu a{display:block;padding:3px 15px;clear:both;font-weight:normal;line-height:18px;color:#333;white-space:nowrap}.dropdown-menu li>a:hover,.dropdown-menu .active>a,.dropdown-menu .active>a:hover{color:#fff;text-decoration:none;background-color:#08c}.open{*z-index:1000}.open>.dropdown-menu{display:block}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{border-top:0;border-bottom:4px solid #000;content:"\2191"}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:1px}.typeahead{margin-top:2px;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #eee;border:1px solid rgba(0,0,0,0.05);-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.05);box-shadow:inset 0 1px 1px rgba(0,0,0,0.05)}.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,0.15)}.well-large{padding:24px;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px}.well-small{padding:9px;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.fade{opacity:0;-webkit-transition:opacity .15s linear;-moz-transition:opacity .15s linear;-ms-transition:opacity .15s linear;-o-transition:opacity .15s linear;transition:opacity .15s linear}.fade.in{opacity:1}.collapse{position:relative;height:0;overflow:hidden;-webkit-transition:height .35s ease;-moz-transition:height .35s ease;-ms-transition:height .35s ease;-o-transition:height .35s ease;transition:height .35s ease}.collapse.in{height:auto}.close{float:right;font-size:20px;font-weight:bold;line-height:18px;color:#000;text-shadow:0 1px 0 #fff;opacity:.2;filter:alpha(opacity=20)}.close:hover{color:#000;text-decoration:none;cursor:pointer;opacity:.4;filter:alpha(opacity=40)}button.close{padding:0;cursor:pointer;background:transparent;border:0;-webkit-appearance:none}.btn{display:inline-block;*display:inline;padding:4px 10px 4px;margin-bottom:0;*margin-left:.3em;font-size:13px;line-height:18px;*line-height:20px;color:#333;text-align:center;text-shadow:0 1px 1px rgba(255,255,255,0.75);vertical-align:middle;cursor:pointer;background-color:#f5f5f5;*background-color:#e6e6e6;background-image:-ms-linear-gradient(top,#fff,#e6e6e6);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fff),to(#e6e6e6));background-image:-webkit-linear-gradient(top,#fff,#e6e6e6);background-image:-o-linear-gradient(top,#fff,#e6e6e6);background-image:linear-gradient(top,#fff,#e6e6e6);background-image:-moz-linear-gradient(top,#fff,#e6e6e6);background-repeat:repeat-x;border:1px solid #ccc;*border:0;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);border-color:#e6e6e6 #e6e6e6 #bfbfbf;border-bottom-color:#b3b3b3;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;filter:progid:dximagetransform.microsoft.gradient(startColorstr='#ffffff',endColorstr='#e6e6e6',GradientType=0);filter:progid:dximagetransform.microsoft.gradient(enabled=false);*zoom:1;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05)}.btn:hover,.btn:active,.btn.active,.btn.disabled,.btn[disabled]{background-color:#e6e6e6;*background-color:#d9d9d9}.btn:active,.btn.active{background-color:#ccc \9}.btn:first-child{*margin-left:0}.btn:hover{color:#333;text-decoration:none;background-color:#e6e6e6;*background-color:#d9d9d9;background-position:0 -15px;-webkit-transition:background-position .1s linear;-moz-transition:background-position .1s linear;-ms-transition:background-position .1s linear;-o-transition:background-position .1s linear;transition:background-position .1s linear}.btn:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn.active,.btn:active{background-color:#e6e6e6;background-color:#d9d9d9 \9;background-image:none;outline:0;-webkit-box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05)}.btn.disabled,.btn[disabled]{cursor:default;background-color:#e6e6e6;background-image:none;opacity:.65;filter:alpha(opacity=65);-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.btn-large{padding:9px 14px;font-size:15px;line-height:normal;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px}.btn-large [class^="icon-"]{margin-top:1px}.btn-small{padding:5px 9px;font-size:11px;line-height:16px}.btn-small [class^="icon-"]{margin-top:-1px}.btn-mini{padding:2px 6px;font-size:11px;line-height:14px}.btn-primary,.btn-primary:hover,.btn-warning,.btn-warning:hover,.btn-danger,.btn-danger:hover,.btn-success,.btn-success:hover,.btn-info,.btn-info:hover,.btn-inverse,.btn-inverse:hover{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25)}.btn-primary.active,.btn-warning.active,.btn-danger.active,.btn-success.active,.btn-info.active,.btn-inverse.active{color:rgba(255,255,255,0.75)}.btn{border-color:#ccc;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25)}.btn-primary{background-color:#0074cc;*background-color:#05c;background-image:-ms-linear-gradient(top,#08c,#05c);background-image:-webkit-gradient(linear,0 0,0 100%,from(#08c),to(#05c));background-image:-webkit-linear-gradient(top,#08c,#05c);background-image:-o-linear-gradient(top,#08c,#05c);background-image:-moz-linear-gradient(top,#08c,#05c);background-image:linear-gradient(top,#08c,#05c);background-repeat:repeat-x;border-color:#05c #05c #003580;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:dximagetransform.microsoft.gradient(startColorstr='#0088cc',endColorstr='#0055cc',GradientType=0);filter:progid:dximagetransform.microsoft.gradient(enabled=false)}.btn-primary:hover,.btn-primary:active,.btn-primary.active,.btn-primary.disabled,.btn-primary[disabled]{background-color:#05c;*background-color:#004ab3}.btn-primary:active,.btn-primary.active{background-color:#004099 \9}.btn-warning{background-color:#faa732;*background-color:#f89406;background-image:-ms-linear-gradient(top,#fbb450,#f89406);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fbb450),to(#f89406));background-image:-webkit-linear-gradient(top,#fbb450,#f89406);background-image:-o-linear-gradient(top,#fbb450,#f89406);background-image:-moz-linear-gradient(top,#fbb450,#f89406);background-image:linear-gradient(top,#fbb450,#f89406);background-repeat:repeat-x;border-color:#f89406 #f89406 #ad6704;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:dximagetransform.microsoft.gradient(startColorstr='#fbb450',endColorstr='#f89406',GradientType=0);filter:progid:dximagetransform.microsoft.gradient(enabled=false)}.btn-warning:hover,.btn-warning:active,.btn-warning.active,.btn-warning.disabled,.btn-warning[disabled]{background-color:#f89406;*background-color:#df8505}.btn-warning:active,.btn-warning.active{background-color:#c67605 \9}.btn-danger{background-color:#da4f49;*background-color:#bd362f;background-image:-ms-linear-gradient(top,#ee5f5b,#bd362f);background-image:-webkit-gradient(linear,0 0,0 100%,from(#ee5f5b),to(#bd362f));background-image:-webkit-linear-gradient(top,#ee5f5b,#bd362f);background-image:-o-linear-gradient(top,#ee5f5b,#bd362f);background-image:-moz-linear-gradient(top,#ee5f5b,#bd362f);background-image:linear-gradient(top,#ee5f5b,#bd362f);background-repeat:repeat-x;border-color:#bd362f #bd362f #802420;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:dximagetransform.microsoft.gradient(startColorstr='#ee5f5b',endColorstr='#bd362f',GradientType=0);filter:progid:dximagetransform.microsoft.gradient(enabled=false)}.btn-danger:hover,.btn-danger:active,.btn-danger.active,.btn-danger.disabled,.btn-danger[disabled]{background-color:#bd362f;*background-color:#a9302a}.btn-danger:active,.btn-danger.active{background-color:#942a25 \9}.btn-success{background-color:#5bb75b;*background-color:#51a351;background-image:-ms-linear-gradient(top,#62c462,#51a351);background-image:-webkit-gradient(linear,0 0,0 100%,from(#62c462),to(#51a351));background-image:-webkit-linear-gradient(top,#62c462,#51a351);background-image:-o-linear-gradient(top,#62c462,#51a351);background-image:-moz-linear-gradient(top,#62c462,#51a351);background-image:linear-gradient(top,#62c462,#51a351);background-repeat:repeat-x;border-color:#51a351 #51a351 #387038;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:dximagetransform.microsoft.gradient(startColorstr='#62c462',endColorstr='#51a351',GradientType=0);filter:progid:dximagetransform.microsoft.gradient(enabled=false)}.btn-success:hover,.btn-success:active,.btn-success.active,.btn-success.disabled,.btn-success[disabled]{background-color:#51a351;*background-color:#499249}.btn-success:active,.btn-success.active{background-color:#408140 \9}.btn-info{background-color:#49afcd;*background-color:#2f96b4;background-image:-ms-linear-gradient(top,#5bc0de,#2f96b4);background-image:-webkit-gradient(linear,0 0,0 100%,from(#5bc0de),to(#2f96b4));background-image:-webkit-linear-gradient(top,#5bc0de,#2f96b4);background-image:-o-linear-gradient(top,#5bc0de,#2f96b4);background-image:-moz-linear-gradient(top,#5bc0de,#2f96b4);background-image:linear-gradient(top,#5bc0de,#2f96b4);background-repeat:repeat-x;border-color:#2f96b4 #2f96b4 #1f6377;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:dximagetransform.microsoft.gradient(startColorstr='#5bc0de',endColorstr='#2f96b4',GradientType=0);filter:progid:dximagetransform.microsoft.gradient(enabled=false)}.btn-info:hover,.btn-info:active,.btn-info.active,.btn-info.disabled,.btn-info[disabled]{background-color:#2f96b4;*background-color:#2a85a0}.btn-info:active,.btn-info.active{background-color:#24748c \9}.btn-inverse{background-color:#414141;*background-color:#222;background-image:-ms-linear-gradient(top,#555,#222);background-image:-webkit-gradient(linear,0 0,0 100%,from(#555),to(#222));background-image:-webkit-linear-gradient(top,#555,#222);background-image:-o-linear-gradient(top,#555,#222);background-image:-moz-linear-gradient(top,#555,#222);background-image:linear-gradient(top,#555,#222);background-repeat:repeat-x;border-color:#222 #222 #000;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:dximagetransform.microsoft.gradient(startColorstr='#555555',endColorstr='#222222',GradientType=0);filter:progid:dximagetransform.microsoft.gradient(enabled=false)}.btn-inverse:hover,.btn-inverse:active,.btn-inverse.active,.btn-inverse.disabled,.btn-inverse[disabled]{background-color:#222;*background-color:#151515}.btn-inverse:active,.btn-inverse.active{background-color:#080808 \9}button.btn,input[type="submit"].btn{*padding-top:2px;*padding-bottom:2px}button.btn::-moz-focus-inner,input[type="submit"].btn::-moz-focus-inner{padding:0;border:0}button.btn.btn-large,input[type="submit"].btn.btn-large{*padding-top:7px;*padding-bottom:7px}button.btn.btn-small,input[type="submit"].btn.btn-small{*padding-top:3px;*padding-bottom:3px}button.btn.btn-mini,input[type="submit"].btn.btn-mini{*padding-top:1px;*padding-bottom:1px}.btn-group{position:relative;*margin-left:.3em;*zoom:1}.btn-group:before,.btn-group:after{display:table;content:""}.btn-group:after{clear:both}.btn-group:first-child{*margin-left:0}.btn-group+.btn-group{margin-left:5px}.btn-toolbar{margin-top:9px;margin-bottom:9px}.btn-toolbar .btn-group{display:inline-block;*display:inline;*zoom:1}.btn-group>.btn{position:relative;float:left;margin-left:-1px;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.btn-group>.btn:first-child{margin-left:0;-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px;-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-bottomleft:4px;-moz-border-radius-topleft:4px}.btn-group>.btn:last-child,.btn-group>.dropdown-toggle{-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px;-moz-border-radius-topright:4px;-moz-border-radius-bottomright:4px}.btn-group>.btn.large:first-child{margin-left:0;-webkit-border-bottom-left-radius:6px;border-bottom-left-radius:6px;-webkit-border-top-left-radius:6px;border-top-left-radius:6px;-moz-border-radius-bottomleft:6px;-moz-border-radius-topleft:6px}.btn-group>.btn.large:last-child,.btn-group>.large.dropdown-toggle{-webkit-border-top-right-radius:6px;border-top-right-radius:6px;-webkit-border-bottom-right-radius:6px;border-bottom-right-radius:6px;-moz-border-radius-topright:6px;-moz-border-radius-bottomright:6px}.btn-group>.btn:hover,.btn-group>.btn:focus,.btn-group>.btn:active,.btn-group>.btn.active{z-index:2}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.dropdown-toggle{*padding-top:4px;padding-right:8px;*padding-bottom:4px;padding-left:8px;-webkit-box-shadow:inset 1px 0 0 rgba(255,255,255,0.125),inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 1px 0 0 rgba(255,255,255,0.125),inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 1px 0 0 rgba(255,255,255,0.125),inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05)}.btn-group>.btn-mini.dropdown-toggle{padding-right:5px;padding-left:5px}.btn-group>.btn-small.dropdown-toggle{*padding-top:4px;*padding-bottom:4px}.btn-group>.btn-large.dropdown-toggle{padding-right:12px;padding-left:12px}.btn-group.open .dropdown-toggle{background-image:none;-webkit-box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05)}.btn-group.open .btn.dropdown-toggle{background-color:#e6e6e6}.btn-group.open .btn-primary.dropdown-toggle{background-color:#05c}.btn-group.open .btn-warning.dropdown-toggle{background-color:#f89406}.btn-group.open .btn-danger.dropdown-toggle{background-color:#bd362f}.btn-group.open .btn-success.dropdown-toggle{background-color:#51a351}.btn-group.open .btn-info.dropdown-toggle{background-color:#2f96b4}.btn-group.open .btn-inverse.dropdown-toggle{background-color:#222}.btn .caret{margin-top:7px;margin-left:0}.btn:hover .caret,.open.btn-group .caret{opacity:1;filter:alpha(opacity=100)}.btn-mini .caret{margin-top:5px}.btn-small .caret{margin-top:6px}.btn-large .caret{margin-top:6px;border-top-width:5px;border-right-width:5px;border-left-width:5px}.dropup .btn-large .caret{border-top:0;border-bottom:5px solid #000}.btn-primary .caret,.btn-warning .caret,.btn-danger .caret,.btn-info .caret,.btn-success .caret,.btn-inverse .caret{border-top-color:#fff;border-bottom-color:#fff;opacity:.75;filter:alpha(opacity=75)}.alert{padding:8px 35px 8px 14px;margin-bottom:18px;color:#c09853;text-shadow:0 1px 0 rgba(255,255,255,0.5);background-color:#fcf8e3;border:1px solid #fbeed5;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.alert-heading{color:inherit}.alert .close{position:relative;top:-2px;right:-21px;line-height:18px}.alert-success{color:#468847;background-color:#dff0d8;border-color:#d6e9c6}.alert-danger,.alert-error{color:#b94a48;background-color:#f2dede;border-color:#eed3d7}.alert-info{color:#3a87ad;background-color:#d9edf7;border-color:#bce8f1}.alert-block{padding-top:14px;padding-bottom:14px}.alert-block>p,.alert-block>ul{margin-bottom:0}.alert-block p+p{margin-top:5px}.nav{margin-bottom:18px;margin-left:0;list-style:none}.nav>li>a{display:block}.nav>li>a:hover{text-decoration:none;background-color:#eee}.nav>.pull-right{float:right}.nav .nav-header{display:block;padding:3px 15px;font-size:11px;font-weight:bold;line-height:18px;color:#999;text-shadow:0 1px 0 rgba(255,255,255,0.5);text-transform:uppercase}.nav li+.nav-header{margin-top:9px}.nav-list{padding-right:15px;padding-left:15px;margin-bottom:0}.nav-list>li>a,.nav-list .nav-header{margin-right:-15px;margin-left:-15px;text-shadow:0 1px 0 rgba(255,255,255,0.5)}.nav-list>li>a{padding:3px 15px}.nav-list>.active>a,.nav-list>.active>a:hover{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.2);background-color:#08c}.nav-list [class^="icon-"]{margin-right:2px}.nav-list .divider{*width:100%;height:1px;margin:8px 1px;*margin:-5px 0 5px;overflow:hidden;background-color:#e5e5e5;border-bottom:1px solid #fff}.nav-tabs,.nav-pills{*zoom:1}.nav-tabs:before,.nav-pills:before,.nav-tabs:after,.nav-pills:after{display:table;content:""}.nav-tabs:after,.nav-pills:after{clear:both}.nav-tabs>li,.nav-pills>li{float:left}.nav-tabs>li>a,.nav-pills>li>a{padding-right:12px;padding-left:12px;margin-right:2px;line-height:14px}.nav-tabs{border-bottom:1px solid #ddd}.nav-tabs>li{margin-bottom:-1px}.nav-tabs>li>a{padding-top:8px;padding-bottom:8px;line-height:18px;border:1px solid transparent;-webkit-border-radius:4px 4px 0 0;-moz-border-radius:4px 4px 0 0;border-radius:4px 4px 0 0}.nav-tabs>li>a:hover{border-color:#eee #eee #ddd}.nav-tabs>.active>a,.nav-tabs>.active>a:hover{color:#555;cursor:default;background-color:#fff;border:1px solid #ddd;border-bottom-color:transparent}.nav-pills>li>a{padding-top:8px;padding-bottom:8px;margin-top:2px;margin-bottom:2px;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px}.nav-pills>.active>a,.nav-pills>.active>a:hover{color:#fff;background-color:#08c}.nav-stacked>li{float:none}.nav-stacked>li>a{margin-right:0}.nav-tabs.nav-stacked{border-bottom:0}.nav-tabs.nav-stacked>li>a{border:1px solid #ddd;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.nav-tabs.nav-stacked>li:first-child>a{-webkit-border-radius:4px 4px 0 0;-moz-border-radius:4px 4px 0 0;border-radius:4px 4px 0 0}.nav-tabs.nav-stacked>li:last-child>a{-webkit-border-radius:0 0 4px 4px;-moz-border-radius:0 0 4px 4px;border-radius:0 0 4px 4px}.nav-tabs.nav-stacked>li>a:hover{z-index:2;border-color:#ddd}.nav-pills.nav-stacked>li>a{margin-bottom:3px}.nav-pills.nav-stacked>li:last-child>a{margin-bottom:1px}.nav-tabs .dropdown-menu{-webkit-border-radius:0 0 5px 5px;-moz-border-radius:0 0 5px 5px;border-radius:0 0 5px 5px}.nav-pills .dropdown-menu{-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.nav-tabs .dropdown-toggle .caret,.nav-pills .dropdown-toggle .caret{margin-top:6px;border-top-color:#08c;border-bottom-color:#08c}.nav-tabs .dropdown-toggle:hover .caret,.nav-pills .dropdown-toggle:hover .caret{border-top-color:#005580;border-bottom-color:#005580}.nav-tabs .active .dropdown-toggle .caret,.nav-pills .active .dropdown-toggle .caret{border-top-color:#333;border-bottom-color:#333}.nav>.dropdown.active>a:hover{color:#000;cursor:pointer}.nav-tabs .open .dropdown-toggle,.nav-pills .open .dropdown-toggle,.nav>li.dropdown.open.active>a:hover{color:#fff;background-color:#999;border-color:#999}.nav li.dropdown.open .caret,.nav li.dropdown.open.active .caret,.nav li.dropdown.open a:hover .caret{border-top-color:#fff;border-bottom-color:#fff;opacity:1;filter:alpha(opacity=100)}.tabs-stacked .open>a:hover{border-color:#999}.tabbable{*zoom:1}.tabbable:before,.tabbable:after{display:table;content:""}.tabbable:after{clear:both}.tab-content{overflow:auto}.tabs-below>.nav-tabs,.tabs-right>.nav-tabs,.tabs-left>.nav-tabs{border-bottom:0}.tab-content>.tab-pane,.pill-content>.pill-pane{display:none}.tab-content>.active,.pill-content>.active{display:block}.tabs-below>.nav-tabs{border-top:1px solid #ddd}.tabs-below>.nav-tabs>li{margin-top:-1px;margin-bottom:0}.tabs-below>.nav-tabs>li>a{-webkit-border-radius:0 0 4px 4px;-moz-border-radius:0 0 4px 4px;border-radius:0 0 4px 4px}.tabs-below>.nav-tabs>li>a:hover{border-top-color:#ddd;border-bottom-color:transparent}.tabs-below>.nav-tabs>.active>a,.tabs-below>.nav-tabs>.active>a:hover{border-color:transparent #ddd #ddd #ddd}.tabs-left>.nav-tabs>li,.tabs-right>.nav-tabs>li{float:none}.tabs-left>.nav-tabs>li>a,.tabs-right>.nav-tabs>li>a{min-width:74px;margin-right:0;margin-bottom:3px}.tabs-left>.nav-tabs{float:left;margin-right:19px;border-right:1px solid #ddd}.tabs-left>.nav-tabs>li>a{margin-right:-1px;-webkit-border-radius:4px 0 0 4px;-moz-border-radius:4px 0 0 4px;border-radius:4px 0 0 4px}.tabs-left>.nav-tabs>li>a:hover{border-color:#eee #ddd #eee #eee}.tabs-left>.nav-tabs .active>a,.tabs-left>.nav-tabs .active>a:hover{border-color:#ddd transparent #ddd #ddd;*border-right-color:#fff}.tabs-right>.nav-tabs{float:right;margin-left:19px;border-left:1px solid #ddd}.tabs-right>.nav-tabs>li>a{margin-left:-1px;-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0}.tabs-right>.nav-tabs>li>a:hover{border-color:#eee #eee #eee #ddd}.tabs-right>.nav-tabs .active>a,.tabs-right>.nav-tabs .active>a:hover{border-color:#ddd #ddd #ddd transparent;*border-left-color:#fff}.navbar{*position:relative;*z-index:2;margin-bottom:18px;overflow:visible}.navbar-inner{min-height:40px;padding-right:20px;padding-left:20px;background-color:#2c2c2c;background-image:-moz-linear-gradient(top,#333,#222);background-image:-ms-linear-gradient(top,#333,#222);background-image:-webkit-gradient(linear,0 0,0 100%,from(#333),to(#222));background-image:-webkit-linear-gradient(top,#333,#222);background-image:-o-linear-gradient(top,#333,#222);background-image:linear-gradient(top,#333,#222);background-repeat:repeat-x;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;filter:progid:dximagetransform.microsoft.gradient(startColorstr='#333333',endColorstr='#222222',GradientType=0);-webkit-box-shadow:0 1px 3px rgba(0,0,0,0.25),inset 0 -1px 0 rgba(0,0,0,0.1);-moz-box-shadow:0 1px 3px rgba(0,0,0,0.25),inset 0 -1px 0 rgba(0,0,0,0.1);box-shadow:0 1px 3px rgba(0,0,0,0.25),inset 0 -1px 0 rgba(0,0,0,0.1)}.navbar .container{width:auto}.nav-collapse.collapse{height:auto}.navbar{color:#999}.navbar .brand:hover{text-decoration:none}.navbar .brand{display:block;float:left;padding:8px 20px 12px;margin-left:-20px;font-size:20px;font-weight:200;line-height:1;color:#999}.navbar .navbar-text{margin-bottom:0;line-height:40px}.navbar .navbar-link{color:#999}.navbar .navbar-link:hover{color:#fff}.navbar .btn,.navbar .btn-group{margin-top:5px}.navbar .btn-group .btn{margin:0}.navbar-form{margin-bottom:0;*zoom:1}.navbar-form:before,.navbar-form:after{display:table;content:""}.navbar-form:after{clear:both}.navbar-form input,.navbar-form select,.navbar-form .radio,.navbar-form .checkbox{margin-top:5px}.navbar-form input,.navbar-form select{display:inline-block;margin-bottom:0}.navbar-form input[type="image"],.navbar-form input[type="checkbox"],.navbar-form input[type="radio"]{margin-top:3px}.navbar-form .input-append,.navbar-form .input-prepend{margin-top:6px;white-space:nowrap}.navbar-form .input-append input,.navbar-form .input-prepend input{margin-top:0}.navbar-search{position:relative;float:left;margin-top:6px;margin-bottom:0}.navbar-search .search-query{padding:4px 9px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:13px;font-weight:normal;line-height:1;color:#fff;background-color:#626262;border:1px solid #151515;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1),0 1px 0 rgba(255,255,255,0.15);-moz-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1),0 1px 0 rgba(255,255,255,0.15);box-shadow:inset 0 1px 2px rgba(0,0,0,0.1),0 1px 0 rgba(255,255,255,0.15);-webkit-transition:none;-moz-transition:none;-ms-transition:none;-o-transition:none;transition:none}.navbar-search .search-query:-moz-placeholder{color:#ccc}.navbar-search .search-query:-ms-input-placeholder{color:#ccc}.navbar-search .search-query::-webkit-input-placeholder{color:#ccc}.navbar-search .search-query:focus,.navbar-search .search-query.focused{padding:5px 10px;color:#333;text-shadow:0 1px 0 #fff;background-color:#fff;border:0;outline:0;-webkit-box-shadow:0 0 3px rgba(0,0,0,0.15);-moz-box-shadow:0 0 3px rgba(0,0,0,0.15);box-shadow:0 0 3px rgba(0,0,0,0.15)}.navbar-fixed-top,.navbar-fixed-bottom{position:fixed;right:0;left:0;z-index:1030;margin-bottom:0}.navbar-fixed-top .navbar-inner,.navbar-fixed-bottom .navbar-inner{padding-right:0;padding-left:0;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:940px}.navbar-fixed-top{top:0}.navbar-fixed-bottom{bottom:0}.navbar .nav{position:relative;left:0;display:block;float:left;margin:0 10px 0 0}.navbar .nav.pull-right{float:right}.navbar .nav>li{display:block;float:left}.navbar .nav>li>a{float:none;padding:9px 10px 11px;line-height:19px;color:#999;text-decoration:none;text-shadow:0 -1px 0 rgba(0,0,0,0.25)}.navbar .btn{display:inline-block;padding:4px 10px 4px;margin:5px 5px 6px;line-height:18px}.navbar .btn-group{padding:5px 5px 6px;margin:0}.navbar .nav>li>a:hover{color:#fff;text-decoration:none;background-color:transparent}.navbar .nav .active>a,.navbar .nav .active>a:hover{color:#fff;text-decoration:none;background-color:#222}.navbar .divider-vertical{width:1px;height:40px;margin:0 9px;overflow:hidden;background-color:#222;border-right:1px solid #333}.navbar .nav.pull-right{margin-right:0;margin-left:10px}.navbar .btn-navbar{display:none;float:right;padding:7px 10px;margin-right:5px;margin-left:5px;background-color:#2c2c2c;*background-color:#222;background-image:-ms-linear-gradient(top,#333,#222);background-image:-webkit-gradient(linear,0 0,0 100%,from(#333),to(#222));background-image:-webkit-linear-gradient(top,#333,#222);background-image:-o-linear-gradient(top,#333,#222);background-image:linear-gradient(top,#333,#222);background-image:-moz-linear-gradient(top,#333,#222);background-repeat:repeat-x;border-color:#222 #222 #000;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:dximagetransform.microsoft.gradient(startColorstr='#333333',endColorstr='#222222',GradientType=0);filter:progid:dximagetransform.microsoft.gradient(enabled=false);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.075);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.075);box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.075)}.navbar .btn-navbar:hover,.navbar .btn-navbar:active,.navbar .btn-navbar.active,.navbar .btn-navbar.disabled,.navbar .btn-navbar[disabled]{background-color:#222;*background-color:#151515}.navbar .btn-navbar:active,.navbar .btn-navbar.active{background-color:#080808 \9}.navbar .btn-navbar .icon-bar{display:block;width:18px;height:2px;background-color:#f5f5f5;-webkit-border-radius:1px;-moz-border-radius:1px;border-radius:1px;-webkit-box-shadow:0 1px 0 rgba(0,0,0,0.25);-moz-box-shadow:0 1px 0 rgba(0,0,0,0.25);box-shadow:0 1px 0 rgba(0,0,0,0.25)}.btn-navbar .icon-bar+.icon-bar{margin-top:3px}.navbar .dropdown-menu:before{position:absolute;top:-7px;left:9px;display:inline-block;border-right:7px solid transparent;border-bottom:7px solid #ccc;border-left:7px solid transparent;border-bottom-color:rgba(0,0,0,0.2);content:''}.navbar .dropdown-menu:after{position:absolute;top:-6px;left:10px;display:inline-block;border-right:6px solid transparent;border-bottom:6px solid #fff;border-left:6px solid transparent;content:''}.navbar-fixed-bottom .dropdown-menu:before{top:auto;bottom:-7px;border-top:7px solid #ccc;border-bottom:0;border-top-color:rgba(0,0,0,0.2)}.navbar-fixed-bottom .dropdown-menu:after{top:auto;bottom:-6px;border-top:6px solid #fff;border-bottom:0}.navbar .nav li.dropdown .dropdown-toggle .caret,.navbar .nav li.dropdown.open .caret{border-top-color:#fff;border-bottom-color:#fff}.navbar .nav li.dropdown.active .caret{opacity:1;filter:alpha(opacity=100)}.navbar .nav li.dropdown.open>.dropdown-toggle,.navbar .nav li.dropdown.active>.dropdown-toggle,.navbar .nav li.dropdown.open.active>.dropdown-toggle{background-color:transparent}.navbar .nav li.dropdown.active>.dropdown-toggle:hover{color:#fff}.navbar .pull-right .dropdown-menu,.navbar .dropdown-menu.pull-right{right:0;left:auto}.navbar .pull-right .dropdown-menu:before,.navbar .dropdown-menu.pull-right:before{right:12px;left:auto}.navbar .pull-right .dropdown-menu:after,.navbar .dropdown-menu.pull-right:after{right:13px;left:auto}.breadcrumb{padding:7px 14px;margin:0 0 18px;list-style:none;background-color:#fbfbfb;background-image:-moz-linear-gradient(top,#fff,#f5f5f5);background-image:-ms-linear-gradient(top,#fff,#f5f5f5);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fff),to(#f5f5f5));background-image:-webkit-linear-gradient(top,#fff,#f5f5f5);background-image:-o-linear-gradient(top,#fff,#f5f5f5);background-image:linear-gradient(top,#fff,#f5f5f5);background-repeat:repeat-x;border:1px solid #ddd;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;filter:progid:dximagetransform.microsoft.gradient(startColorstr='#ffffff',endColorstr='#f5f5f5',GradientType=0);-webkit-box-shadow:inset 0 1px 0 #fff;-moz-box-shadow:inset 0 1px 0 #fff;box-shadow:inset 0 1px 0 #fff}.breadcrumb li{display:inline-block;*display:inline;text-shadow:0 1px 0 #fff;*zoom:1}.breadcrumb .divider{padding:0 5px;color:#999}.breadcrumb .active a{color:#333}.pagination{height:36px;margin:18px 0}.pagination ul{display:inline-block;*display:inline;margin-bottom:0;margin-left:0;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;*zoom:1;-webkit-box-shadow:0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:0 1px 2px rgba(0,0,0,0.05);box-shadow:0 1px 2px rgba(0,0,0,0.05)}.pagination li{display:inline}.pagination a{float:left;padding:0 14px;line-height:34px;text-decoration:none;border:1px solid #ddd;border-left-width:0}.pagination a:hover,.pagination .active a{background-color:#f5f5f5}.pagination .active a{color:#999;cursor:default}.pagination .disabled span,.pagination .disabled a,.pagination .disabled a:hover{color:#999;cursor:default;background-color:transparent}.pagination li:first-child a{border-left-width:1px;-webkit-border-radius:3px 0 0 3px;-moz-border-radius:3px 0 0 3px;border-radius:3px 0 0 3px}.pagination li:last-child a{-webkit-border-radius:0 3px 3px 0;-moz-border-radius:0 3px 3px 0;border-radius:0 3px 3px 0}.pagination-centered{text-align:center}.pagination-right{text-align:right}.pager{margin-bottom:18px;margin-left:0;text-align:center;list-style:none;*zoom:1}.pager:before,.pager:after{display:table;content:""}.pager:after{clear:both}.pager li{display:inline}.pager a{display:inline-block;padding:5px 14px;background-color:#fff;border:1px solid #ddd;-webkit-border-radius:15px;-moz-border-radius:15px;border-radius:15px}.pager a:hover{text-decoration:none;background-color:#f5f5f5}.pager .next a{float:right}.pager .previous a{float:left}.pager .disabled a,.pager .disabled a:hover{color:#999;cursor:default;background-color:#fff}.modal-open .dropdown-menu{z-index:2050}.modal-open .dropdown.open{*z-index:2050}.modal-open .popover{z-index:2060}.modal-open .tooltip{z-index:2070}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000}.modal-backdrop.fade{opacity:0}.modal-backdrop,.modal-backdrop.fade.in{opacity:.8;filter:alpha(opacity=80)}.modal{position:fixed;top:50%;left:50%;z-index:1050;width:560px;margin:-250px 0 0 -280px;overflow:auto;background-color:#fff;border:1px solid #999;border:1px solid rgba(0,0,0,0.3);*border:1px solid #999;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 3px 7px rgba(0,0,0,0.3);-moz-box-shadow:0 3px 7px rgba(0,0,0,0.3);box-shadow:0 3px 7px rgba(0,0,0,0.3);-webkit-background-clip:padding-box;-moz-background-clip:padding-box;background-clip:padding-box}.modal.fade{top:-25%;-webkit-transition:opacity .3s linear,top .3s ease-out;-moz-transition:opacity .3s linear,top .3s ease-out;-ms-transition:opacity .3s linear,top .3s ease-out;-o-transition:opacity .3s linear,top .3s ease-out;transition:opacity .3s linear,top .3s ease-out}.modal.fade.in{top:50%}.modal-header{padding:9px 15px;border-bottom:1px solid #eee}.modal-header .close{margin-top:2px}.modal-body{max-height:400px;padding:15px;overflow-y:auto}.modal-form{margin-bottom:0}.modal-footer{padding:14px 15px 15px;margin-bottom:0;text-align:right;background-color:#f5f5f5;border-top:1px solid #ddd;-webkit-border-radius:0 0 6px 6px;-moz-border-radius:0 0 6px 6px;border-radius:0 0 6px 6px;*zoom:1;-webkit-box-shadow:inset 0 1px 0 #fff;-moz-box-shadow:inset 0 1px 0 #fff;box-shadow:inset 0 1px 0 #fff}.modal-footer:before,.modal-footer:after{display:table;content:""}.modal-footer:after{clear:both}.modal-footer .btn+.btn{margin-bottom:0;margin-left:5px}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.tooltip{position:absolute;z-index:1020;display:block;padding:5px;font-size:11px;opacity:0;filter:alpha(opacity=0);visibility:visible}.tooltip.in{opacity:.8;filter:alpha(opacity=80)}.tooltip.top{margin-top:-2px}.tooltip.right{margin-left:2px}.tooltip.bottom{margin-top:2px}.tooltip.left{margin-left:-2px}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-top:5px solid #000;border-right:5px solid transparent;border-left:5px solid transparent}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-top:5px solid transparent;border-bottom:5px solid transparent;border-left:5px solid #000}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-right:5px solid transparent;border-bottom:5px solid #000;border-left:5px solid transparent}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-top:5px solid transparent;border-right:5px solid #000;border-bottom:5px solid transparent}.tooltip-inner{max-width:200px;padding:3px 8px;color:#fff;text-align:center;text-decoration:none;background-color:#000;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.tooltip-arrow{position:absolute;width:0;height:0}.popover{position:absolute;top:0;left:0;z-index:1010;display:none;padding:5px}.popover.top{margin-top:-5px}.popover.right{margin-left:5px}.popover.bottom{margin-top:5px}.popover.left{margin-left:-5px}.popover.top .arrow{bottom:0;left:50%;margin-left:-5px;border-top:5px solid #000;border-right:5px solid transparent;border-left:5px solid transparent}.popover.right .arrow{top:50%;left:0;margin-top:-5px;border-top:5px solid transparent;border-right:5px solid #000;border-bottom:5px solid transparent}.popover.bottom .arrow{top:0;left:50%;margin-left:-5px;border-right:5px solid transparent;border-bottom:5px solid #000;border-left:5px solid transparent}.popover.left .arrow{top:50%;right:0;margin-top:-5px;border-top:5px solid transparent;border-bottom:5px solid transparent;border-left:5px solid #000}.popover .arrow{position:absolute;width:0;height:0}.popover-inner{width:280px;padding:3px;overflow:hidden;background:#000;background:rgba(0,0,0,0.8);-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 3px 7px rgba(0,0,0,0.3);-moz-box-shadow:0 3px 7px rgba(0,0,0,0.3);box-shadow:0 3px 7px rgba(0,0,0,0.3)}.popover-title{padding:9px 15px;line-height:1;background-color:#f5f5f5;border-bottom:1px solid #eee;-webkit-border-radius:3px 3px 0 0;-moz-border-radius:3px 3px 0 0;border-radius:3px 3px 0 0}.popover-content{padding:14px;background-color:#fff;-webkit-border-radius:0 0 3px 3px;-moz-border-radius:0 0 3px 3px;border-radius:0 0 3px 3px;-webkit-background-clip:padding-box;-moz-background-clip:padding-box;background-clip:padding-box}.popover-content p,.popover-content ul,.popover-content ol{margin-bottom:0}.thumbnails{margin-left:-20px;list-style:none;*zoom:1}.thumbnails:before,.thumbnails:after{display:table;content:""}.thumbnails:after{clear:both}.row-fluid .thumbnails{margin-left:0}.thumbnails>li{float:left;margin-bottom:18px;margin-left:20px}.thumbnail{display:block;padding:4px;line-height:1;border:1px solid #ddd;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:0 1px 1px rgba(0,0,0,0.075);-moz-box-shadow:0 1px 1px rgba(0,0,0,0.075);box-shadow:0 1px 1px rgba(0,0,0,0.075)}a.thumbnail:hover{border-color:#08c;-webkit-box-shadow:0 1px 4px rgba(0,105,214,0.25);-moz-box-shadow:0 1px 4px rgba(0,105,214,0.25);box-shadow:0 1px 4px rgba(0,105,214,0.25)}.thumbnail>img{display:block;max-width:100%;margin-right:auto;margin-left:auto}.thumbnail .caption{padding:9px}.label,.badge{font-size:10.998px;font-weight:bold;line-height:14px;color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);white-space:nowrap;vertical-align:baseline;background-color:#999}.label{padding:1px 4px 2px;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.badge{padding:1px 9px 2px;-webkit-border-radius:9px;-moz-border-radius:9px;border-radius:9px}a.label:hover,a.badge:hover{color:#fff;text-decoration:none;cursor:pointer}.label-important,.badge-important{background-color:#b94a48}.label-important[href],.badge-important[href]{background-color:#953b39}.label-warning,.badge-warning{background-color:#f89406}.label-warning[href],.badge-warning[href]{background-color:#c67605}.label-success,.badge-success{background-color:#468847}.label-success[href],.badge-success[href]{background-color:#356635}.label-info,.badge-info{background-color:#3a87ad}.label-info[href],.badge-info[href]{background-color:#2d6987}.label-inverse,.badge-inverse{background-color:#333}.label-inverse[href],.badge-inverse[href]{background-color:#1a1a1a}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-moz-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-ms-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-o-keyframes progress-bar-stripes{from{background-position:0 0}to{background-position:40px 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.progress{height:18px;margin-bottom:18px;overflow:hidden;background-color:#f7f7f7;background-image:-moz-linear-gradient(top,#f5f5f5,#f9f9f9);background-image:-ms-linear-gradient(top,#f5f5f5,#f9f9f9);background-image:-webkit-gradient(linear,0 0,0 100%,from(#f5f5f5),to(#f9f9f9));background-image:-webkit-linear-gradient(top,#f5f5f5,#f9f9f9);background-image:-o-linear-gradient(top,#f5f5f5,#f9f9f9);background-image:linear-gradient(top,#f5f5f5,#f9f9f9);background-repeat:repeat-x;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;filter:progid:dximagetransform.microsoft.gradient(startColorstr='#f5f5f5',endColorstr='#f9f9f9',GradientType=0);-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1);-moz-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1);box-shadow:inset 0 1px 2px rgba(0,0,0,0.1)}.progress .bar{width:0;height:18px;font-size:12px;color:#fff;text-align:center;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#0e90d2;background-image:-moz-linear-gradient(top,#149bdf,#0480be);background-image:-webkit-gradient(linear,0 0,0 100%,from(#149bdf),to(#0480be));background-image:-webkit-linear-gradient(top,#149bdf,#0480be);background-image:-o-linear-gradient(top,#149bdf,#0480be);background-image:linear-gradient(top,#149bdf,#0480be);background-image:-ms-linear-gradient(top,#149bdf,#0480be);background-repeat:repeat-x;filter:progid:dximagetransform.microsoft.gradient(startColorstr='#149bdf',endColorstr='#0480be',GradientType=0);-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);-moz-box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);-webkit-box-sizing:border-box;-moz-box-sizing:border-box;-ms-box-sizing:border-box;box-sizing:border-box;-webkit-transition:width .6s ease;-moz-transition:width .6s ease;-ms-transition:width .6s ease;-o-transition:width .6s ease;transition:width .6s ease}.progress-striped .bar{background-color:#149bdf;background-image:-o-linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-webkit-linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-ms-linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);-webkit-background-size:40px 40px;-moz-background-size:40px 40px;-o-background-size:40px 40px;background-size:40px 40px}.progress.active .bar{-webkit-animation:progress-bar-stripes 2s linear infinite;-moz-animation:progress-bar-stripes 2s linear infinite;-ms-animation:progress-bar-stripes 2s linear infinite;-o-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress-danger .bar{background-color:#dd514c;background-image:-moz-linear-gradient(top,#ee5f5b,#c43c35);background-image:-ms-linear-gradient(top,#ee5f5b,#c43c35);background-image:-webkit-gradient(linear,0 0,0 100%,from(#ee5f5b),to(#c43c35));background-image:-webkit-linear-gradient(top,#ee5f5b,#c43c35);background-image:-o-linear-gradient(top,#ee5f5b,#c43c35);background-image:linear-gradient(top,#ee5f5b,#c43c35);background-repeat:repeat-x;filter:progid:dximagetransform.microsoft.gradient(startColorstr='#ee5f5b',endColorstr='#c43c35',GradientType=0)}.progress-danger.progress-striped .bar{background-color:#ee5f5b;background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-ms-linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent)}.progress-success .bar{background-color:#5eb95e;background-image:-moz-linear-gradient(top,#62c462,#57a957);background-image:-ms-linear-gradient(top,#62c462,#57a957);background-image:-webkit-gradient(linear,0 0,0 100%,from(#62c462),to(#57a957));background-image:-webkit-linear-gradient(top,#62c462,#57a957);background-image:-o-linear-gradient(top,#62c462,#57a957);background-image:linear-gradient(top,#62c462,#57a957);background-repeat:repeat-x;filter:progid:dximagetransform.microsoft.gradient(startColorstr='#62c462',endColorstr='#57a957',GradientType=0)}.progress-success.progress-striped .bar{background-color:#62c462;background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-ms-linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent)}.progress-info .bar{background-color:#4bb1cf;background-image:-moz-linear-gradient(top,#5bc0de,#339bb9);background-image:-ms-linear-gradient(top,#5bc0de,#339bb9);background-image:-webkit-gradient(linear,0 0,0 100%,from(#5bc0de),to(#339bb9));background-image:-webkit-linear-gradient(top,#5bc0de,#339bb9);background-image:-o-linear-gradient(top,#5bc0de,#339bb9);background-image:linear-gradient(top,#5bc0de,#339bb9);background-repeat:repeat-x;filter:progid:dximagetransform.microsoft.gradient(startColorstr='#5bc0de',endColorstr='#339bb9',GradientType=0)}.progress-info.progress-striped .bar{background-color:#5bc0de;background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-ms-linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent)}.progress-warning .bar{background-color:#faa732;background-image:-moz-linear-gradient(top,#fbb450,#f89406);background-image:-ms-linear-gradient(top,#fbb450,#f89406);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fbb450),to(#f89406));background-image:-webkit-linear-gradient(top,#fbb450,#f89406);background-image:-o-linear-gradient(top,#fbb450,#f89406);background-image:linear-gradient(top,#fbb450,#f89406);background-repeat:repeat-x;filter:progid:dximagetransform.microsoft.gradient(startColorstr='#fbb450',endColorstr='#f89406',GradientType=0)}.progress-warning.progress-striped .bar{background-color:#fbb450;background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-ms-linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent)}.accordion{margin-bottom:18px}.accordion-group{margin-bottom:2px;border:1px solid #e5e5e5;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.accordion-heading{border-bottom:0}.accordion-heading .accordion-toggle{display:block;padding:8px 15px}.accordion-toggle{cursor:pointer}.accordion-inner{padding:9px 15px;border-top:1px solid #e5e5e5}.carousel{position:relative;margin-bottom:18px;line-height:1}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel .item{position:relative;display:none;-webkit-transition:.6s ease-in-out left;-moz-transition:.6s ease-in-out left;-ms-transition:.6s ease-in-out left;-o-transition:.6s ease-in-out left;transition:.6s ease-in-out left}.carousel .item>img{display:block;line-height:1}.carousel .active,.carousel .next,.carousel .prev{display:block}.carousel .active{left:0}.carousel .next,.carousel .prev{position:absolute;top:0;width:100%}.carousel .next{left:100%}.carousel .prev{left:-100%}.carousel .next.left,.carousel .prev.right{left:0}.carousel .active.left{left:-100%}.carousel .active.right{left:100%}.carousel-control{position:absolute;top:40%;left:15px;width:40px;height:40px;margin-top:-20px;font-size:60px;font-weight:100;line-height:30px;color:#fff;text-align:center;background:#222;border:3px solid #fff;-webkit-border-radius:23px;-moz-border-radius:23px;border-radius:23px;opacity:.5;filter:alpha(opacity=50)}.carousel-control.right{right:15px;left:auto}.carousel-control:hover{color:#fff;text-decoration:none;opacity:.9;filter:alpha(opacity=90)}.carousel-caption{position:absolute;right:0;bottom:0;left:0;padding:10px 15px 5px;background:#333;background:rgba(0,0,0,0.75)}.carousel-caption h4,.carousel-caption p{color:#fff}.hero-unit{padding:60px;margin-bottom:30px;background-color:#eee;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px}.hero-unit h1{margin-bottom:0;font-size:60px;line-height:1;letter-spacing:-1px;color:inherit}.hero-unit p{font-size:18px;font-weight:200;line-height:27px;color:inherit}.pull-right{float:right}.pull-left{float:left}.hide{display:none}.show{display:block}.invisible{visibility:hidden} - - input.field-error, textarea.field-error { border: 1px solid #B94A48; } \ No newline at end of file diff --git a/spring-boot-samples/spring-boot-sample-web-secure-github/src/main/resources/static/index.html b/spring-boot-samples/spring-boot-sample-web-secure-github/src/main/resources/static/index.html deleted file mode 100644 index 3351b2e32c..0000000000 --- a/spring-boot-samples/spring-boot-sample-web-secure-github/src/main/resources/static/index.html +++ /dev/null @@ -1,21 +0,0 @@ - - - -Title - - - -
- -

Super Special Greeting

-
Hello World
-
- - diff --git a/spring-boot-samples/spring-boot-sample-web-secure-github/src/test/java/sample/web/secure/github/SampleGithubApplicationTests.java b/spring-boot-samples/spring-boot-sample-web-secure-github/src/test/java/sample/web/secure/github/SampleGithubApplicationTests.java deleted file mode 100644 index 71bfabf28a..0000000000 --- a/spring-boot-samples/spring-boot-sample-web-secure-github/src/test/java/sample/web/secure/github/SampleGithubApplicationTests.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright 2012-2017 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package sample.web.secure.github; - -import java.net.URI; - -import org.junit.Test; -import org.junit.runner.RunWith; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; -import org.springframework.boot.test.web.client.TestRestTemplate; -import org.springframework.boot.web.server.LocalServerPort; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.test.annotation.DirtiesContext; -import org.springframework.test.context.junit4.SpringRunner; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Basic integration tests for GitHub SSO application. - * - * @author Dave Syer - * @author Andy Wilkinson - */ -@RunWith(SpringRunner.class) -@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT) -@DirtiesContext -public class SampleGithubApplicationTests { - - @LocalServerPort - private int port; - - @Autowired - private TestRestTemplate restTemplate; - - @Test - public void everythingIsSecuredByDefault() throws Exception { - ResponseEntity entity = this.restTemplate.getForEntity("/", Void.class); - assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.FOUND); - assertThat(entity.getHeaders().getLocation()) - .isEqualTo(URI.create("http://localhost:" + this.port + "/login")); - } - - @Test - public void loginRedirectsToGithub() throws Exception { - ResponseEntity entity = this.restTemplate.getForEntity("/login", - Void.class); - assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.FOUND); - assertThat(entity.getHeaders().getLocation().toString()) - .startsWith("https://github.com/login/oauth"); - } - -}