diff --git a/pom.xml b/pom.xml
index deef806..092ee3a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -72,6 +72,11 @@
eureka-core
true
+
+ com.netflix.zuul
+ zuul-core
+ true
+
org.springframework.boot
spring-boot-starter-test
diff --git a/src/main/java/org/springframework/platform/cloudfoundry/proxy/CloudfoundryProxyConfiguration.java b/src/main/java/org/springframework/platform/cloudfoundry/proxy/CloudfoundryProxyConfiguration.java
new file mode 100644
index 0000000..7bfebba
--- /dev/null
+++ b/src/main/java/org/springframework/platform/cloudfoundry/proxy/CloudfoundryProxyConfiguration.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2013-2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * 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.platform.cloudfoundry.proxy;
+
+import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
+import org.springframework.boot.autoconfigure.security.SecurityProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.oauth2.config.annotation.web.configuration.EnableOAuth2Client;
+
+import com.netflix.zuul.ZuulFilter;
+
+/**
+ * @author Dave Syer
+ *
+ */
+@Configuration
+@ConditionalOnClass({ ZuulFilter.class, EnableOAuth2Client.class, SecurityProperties.class })
+@ConditionalOnWebApplication
+public class CloudfoundryProxyConfiguration {
+
+ @Bean
+ public CloudfoundryTokenFilter cloudfoundryTokenFilter() {
+ return new CloudfoundryTokenFilter();
+ }
+
+}
diff --git a/src/main/java/org/springframework/platform/cloudfoundry/proxy/CloudfoundryTokenFilter.java b/src/main/java/org/springframework/platform/cloudfoundry/proxy/CloudfoundryTokenFilter.java
new file mode 100644
index 0000000..0a9dd18
--- /dev/null
+++ b/src/main/java/org/springframework/platform/cloudfoundry/proxy/CloudfoundryTokenFilter.java
@@ -0,0 +1,46 @@
+package org.springframework.platform.cloudfoundry.proxy;
+
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.oauth2.provider.OAuth2Authentication;
+import org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationDetails;
+
+import com.netflix.zuul.ZuulFilter;
+import com.netflix.zuul.context.RequestContext;
+
+public class CloudfoundryTokenFilter extends ZuulFilter {
+
+ private static final String ACCESS_TOKEN = "ACCESS_TOKEN";
+
+ @Override
+ public int filterOrder() {
+ return 10;
+ }
+
+ @Override
+ public String filterType() {
+ return "pre";
+ }
+
+ @Override
+ public boolean shouldFilter() {
+ Authentication auth = SecurityContextHolder.getContext().getAuthentication();
+ if (auth instanceof OAuth2Authentication) {
+ Object details = auth.getDetails();
+ if (details instanceof OAuth2AuthenticationDetails) {
+ OAuth2AuthenticationDetails oauth = (OAuth2AuthenticationDetails) details;
+ RequestContext ctx = RequestContext.getCurrentContext();
+ ctx.set(ACCESS_TOKEN, oauth.getTokenValue());
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public Object run() {
+ RequestContext ctx = RequestContext.getCurrentContext();
+ ctx.addZuulRequestHeader("authorization", "Bearer " + ctx.get(ACCESS_TOKEN));
+ return null;
+ }
+}
diff --git a/src/main/java/org/springframework/platform/cloudfoundry/resource/CloudfoundryResourceConfiguration.java b/src/main/java/org/springframework/platform/cloudfoundry/resource/CloudfoundryResourceConfiguration.java
new file mode 100644
index 0000000..0d6b1e7
--- /dev/null
+++ b/src/main/java/org/springframework/platform/cloudfoundry/resource/CloudfoundryResourceConfiguration.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright 2013-2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * 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.platform.cloudfoundry.resource;
+
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.config.BeanPostProcessor;
+import org.springframework.boot.actuate.autoconfigure.ManagementServerProperties;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
+import org.springframework.boot.autoconfigure.security.SecurityProperties;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+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.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.security.oauth2.provider.token.RemoteTokenServices;
+import org.springframework.security.oauth2.provider.token.ResourceServerTokenServices;
+import org.springframework.util.ClassUtils;
+
+/**
+ * @author Dave Syer
+ *
+ */
+@Configuration
+@ConditionalOnExpression("'${cloudfoundry.resource.clientId:${vcap.services.resource.credentials.clientId:}}'!=''")
+@ConditionalOnClass({ EnableResourceServer.class, SecurityProperties.class })
+@ConditionalOnWebApplication
+@EnableResourceServer
+@EnableConfigurationProperties(CloudfoundryResourceProperties.class)
+public class CloudfoundryResourceConfiguration {
+
+ @Autowired
+ private CloudfoundryResourceProperties resource;
+
+ @Bean
+ @ConditionalOnMissingBean(ResourceServerConfigurer.class)
+ public ResourceServerConfigurer resourceServer() {
+ return new ResourceSecurityConfigurer(resource);
+ }
+
+ @Bean
+ @ConditionalOnMissingBean(ResourceServerTokenServices.class)
+ public ResourceServerTokenServices tokenServices() {
+ RemoteTokenServices services = new RemoteTokenServices();
+ services.setCheckTokenEndpointUrl(resource.getTokenInfoUri());
+ services.setClientId(resource.getClientId());
+ services.setClientSecret(resource.getClientSecret());
+ return services;
+ }
+
+
+
+ protected static class ResourceSecurityConfigurer extends ResourceServerConfigurerAdapter {
+
+ private CloudfoundryResourceProperties resource;
+
+ @Autowired
+ public ResourceSecurityConfigurer(CloudfoundryResourceProperties resource) {
+ this.resource = resource;
+ }
+
+ @Override
+ public void configure(ResourceServerSecurityConfigurer resources)
+ throws Exception {
+ resources.resourceId(resource.getClientId());
+ }
+
+ @Override
+ public void configure(HttpSecurity http) throws Exception {
+ http.authorizeRequests().anyRequest().authenticated();
+ }
+
+ }
+
+ @Configuration
+ protected static class ResourceServerOrderProcessor implements BeanPostProcessor {
+
+ @Override
+ public Object postProcessAfterInitialization(Object bean, String beanName)
+ throws BeansException {
+ if (bean instanceof ResourceServerConfiguration) {
+ ResourceServerConfiguration configuration = (ResourceServerConfiguration) bean;
+ configuration.setOrder(getOrder());
+ }
+ return bean;
+ }
+
+ @Override
+ public Object postProcessBeforeInitialization(Object bean, String beanName)
+ throws BeansException {
+ return bean;
+ }
+
+ private int getOrder() {
+ if (ClassUtils
+ .isPresent(
+ "org.springframework.boot.actuate.autoconfigure.ManagementServerProperties",
+ null)) {
+ return ManagementServerProperties.ACCESS_OVERRIDE_ORDER - 10;
+ }
+ return SecurityProperties.ACCESS_OVERRIDE_ORDER - 10;
+ }
+
+ }
+
+}
diff --git a/src/main/java/org/springframework/platform/cloudfoundry/resource/CloudfoundryResourceProperties.java b/src/main/java/org/springframework/platform/cloudfoundry/resource/CloudfoundryResourceProperties.java
new file mode 100644
index 0000000..8d27781
--- /dev/null
+++ b/src/main/java/org/springframework/platform/cloudfoundry/resource/CloudfoundryResourceProperties.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2013-2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * 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.platform.cloudfoundry.resource;
+
+import lombok.Data;
+
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.util.StringUtils;
+import org.springframework.validation.Errors;
+import org.springframework.validation.Validator;
+
+/**
+ * @author Dave Syer
+ *
+ */
+@ConfigurationProperties("cloudfoundry.resource")
+@Data
+public class CloudfoundryResourceProperties implements Validator {
+
+ private String serviceId = "resource";
+
+ @Value("${vcap.services.${cloudfoundry.resource.serviceId:resource}.credentials.clientId:}")
+ private String clientId;
+
+ @Value("${vcap.services.${cloudfoundry.resource.serviceId:resource}.credentials.clientSecret:}")
+ private String clientSecret;
+
+ @Value("${vcap.services.${cloudfoundry.resource.serviceId:resource}.credentials.tokenInfoUri:}")
+ private String tokenInfoUri;
+
+
+ @Override
+ public boolean supports(Class> clazz) {
+ return CloudfoundryResourceProperties.class.isAssignableFrom(clazz);
+ }
+
+ @Override
+ public void validate(Object target, Errors errors) {
+ CloudfoundryResourceProperties resource = (CloudfoundryResourceProperties) target;
+ if (StringUtils.hasText(resource.getClientId())) {
+ if (!StringUtils.hasText(resource.getTokenInfoUri())) {
+ errors.rejectValue("tokenInfoUri", "missing.tokenInfoUri", "Missing tokenInfoUri");
+ }
+ if (!StringUtils.hasText(resource.getClientSecret())) {
+ errors.rejectValue("clientSecret", "missing.clientSecret", "Missing clientSecret");
+ }
+ }
+ }
+}
diff --git a/src/main/java/org/springframework/platform/cloudfoundry/resource/EnableCloudfoundryResource.java b/src/main/java/org/springframework/platform/cloudfoundry/resource/EnableCloudfoundryResource.java
new file mode 100644
index 0000000..e1c85a6
--- /dev/null
+++ b/src/main/java/org/springframework/platform/cloudfoundry/resource/EnableCloudfoundryResource.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2013-2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * 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.platform.cloudfoundry.resource;
+
+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.context.annotation.Import;
+
+/**
+ * @author Dave Syer
+ *
+ */
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+@Import(CloudfoundryResourceConfiguration.class)
+public @interface EnableCloudfoundryResource {
+
+}
diff --git a/src/main/java/org/springframework/platform/cloudfoundry/sso/CloudfoundrySsoConfiguration.java b/src/main/java/org/springframework/platform/cloudfoundry/sso/CloudfoundrySsoConfiguration.java
index 6a9b451..632f18c 100644
--- a/src/main/java/org/springframework/platform/cloudfoundry/sso/CloudfoundrySsoConfiguration.java
+++ b/src/main/java/org/springframework/platform/cloudfoundry/sso/CloudfoundrySsoConfiguration.java
@@ -63,7 +63,7 @@ import org.springframework.util.ClassUtils;
*
*/
@Configuration
-@ConditionalOnExpression("'${cloudfoundry.sso.tokenUri:${vcap.services.sso.credentials.tokenUri:}}'!=''")
+@ConditionalOnExpression("'${cloudfoundry.sso.clientId:${vcap.services.sso.credentials.clientId:}}'!=''")
@ConditionalOnClass({ EnableOAuth2Client.class, SecurityProperties.class })
@ConditionalOnWebApplication
@EnableOAuth2Client
diff --git a/src/main/java/org/springframework/platform/cloudfoundry/sso/CloudfoundrySsoProperties.java b/src/main/java/org/springframework/platform/cloudfoundry/sso/CloudfoundrySsoProperties.java
index 4477226..51e07c2 100644
--- a/src/main/java/org/springframework/platform/cloudfoundry/sso/CloudfoundrySsoProperties.java
+++ b/src/main/java/org/springframework/platform/cloudfoundry/sso/CloudfoundrySsoProperties.java
@@ -71,12 +71,12 @@ public class CloudfoundrySsoProperties implements Validator {
@Override
public void validate(Object target, Errors errors) {
CloudfoundrySsoProperties sso = (CloudfoundrySsoProperties) target;
- if (StringUtils.hasText(sso.getTokenUri())) {
+ if (StringUtils.hasText(sso.getClientId())) {
if (!StringUtils.hasText(sso.getAuthorizationUri())) {
errors.rejectValue("authorizeUri", "missing.authorizeUri", "Missing authorizeUri");
}
- if (!StringUtils.hasText(sso.getClientId())) {
- errors.rejectValue("clientId", "missing.clientId", "Missing clientId");
+ if (!StringUtils.hasText(sso.getTokenUri())) {
+ errors.rejectValue("tokenUri", "missing.tokenUri", "Missing tokenUri");
}
if (!StringUtils.hasText(sso.getClientSecret())) {
errors.rejectValue("clientSecret", "missing.clientSecret", "Missing clientSecret");
diff --git a/src/main/resources/META-INF/spring.factories b/src/main/resources/META-INF/spring.factories
index 5ba9287..23aff23 100644
--- a/src/main/resources/META-INF/spring.factories
+++ b/src/main/resources/META-INF/spring.factories
@@ -1,2 +1,3 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
-org.springframework.platform.cloudfoundry.broker.configuration.ServiceBrokerAutoConfiguration
\ No newline at end of file
+org.springframework.platform.cloudfoundry.broker.configuration.ServiceBrokerAutoConfiguration,\
+org.springframework.platform.cloudfoundry.proxy.CloudfoundryProxyConfiguration
\ No newline at end of file
diff --git a/src/test/java/org/springframework/platform/cloudfoundry/broker/sample/ApplicationTests.java b/src/test/java/org/springframework/platform/cloudfoundry/broker/sample/ApplicationTests.java
index 30bd0f8..ff7d7d9 100644
--- a/src/test/java/org/springframework/platform/cloudfoundry/broker/sample/ApplicationTests.java
+++ b/src/test/java/org/springframework/platform/cloudfoundry/broker/sample/ApplicationTests.java
@@ -33,16 +33,18 @@ import org.springframework.test.context.web.WebAppConfiguration;
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = Application.class)
@WebAppConfiguration
-@IntegrationTest("server.port=0")
+@IntegrationTest({ "server.port=0", "security.basic.enabled=false",
+ "cloudfoundry.sso.clientId=", "cloudfoundry.resource.clientId=" })
public class ApplicationTests {
-
+
@Value("${local.server.port}")
private int port = 0;
@Test
public void catalogLoads() {
@SuppressWarnings("rawtypes")
- ResponseEntity