Move VCAP_SERVICES processing over from spring-cloud-security
This commit is contained in:
21
README.adoc
21
README.adoc
@@ -8,10 +8,17 @@ middlware that you "bind" to an app, essentially providing it with an
|
||||
environment variable containing credentials (e.g. the location and
|
||||
username to use for the service).
|
||||
|
||||
Add this project as a dependency to any Spring Cloud UI app or REST
|
||||
service and deploy to Cloudfoundry. If you use Spring Cloud Security
|
||||
OAuth2 features this will make them bindable to Cloud Foundry services
|
||||
instead of enironment properties in `spring.oauth2.*`. For a UI app you can
|
||||
declare `@EnableOAuth2Sso` and bind to a service called "sso", and for
|
||||
a service you can add `@EnableOAuth2Resource` and bind to a service
|
||||
called "resource" (see below for how to change the names).
|
||||
The `spring-cloud-cloudfoundry-web` project provides basic support for
|
||||
some enhanced features of webapps in Cloud Foundry: binding
|
||||
automatically to single-sign-on services and optionally enabling
|
||||
sticky routing for discovery.
|
||||
|
||||
The `spring-cloud-cloudfoundry-discovery` project provides an
|
||||
implementation of Spring Cloud Commons `DiscoveryClient` so you can
|
||||
`@EnableDiscoveryClient` and provide your credentials as
|
||||
`spring.cloud.cloudfoundry.discovery.[email,password]` and then you
|
||||
can use the `DiscoveryClient` directly or via a `LoadBalancerClient`
|
||||
(also `*.url` if you are not connecting to [Pivotal Web
|
||||
Services](https://run.pivotal.io)).
|
||||
|
||||
> NOTE: if you are looking for a way to bind to services then this is the wrong library. Check out the [Spring Cloud Connectors](https://github.com/spring-cloud/spring-cloud-connectors) instead.
|
||||
@@ -6,7 +6,15 @@ middlware that you "bind" to an app, essentially providing it with an
|
||||
environment variable containing credentials (e.g. the location and
|
||||
username to use for the service).
|
||||
|
||||
This project provides an implementation of Spring Cloud Commons `DiscoveryClient` so you can `@EnableDiscoveryClient`
|
||||
and provide your credentials as `spring.cloud.cloudfoundry.discovery.[email,password]` and then you can use the
|
||||
`DiscoveryClient` directly or via a `LoadBalancerClient` (also `*.url` if you are not connecting to [Pivotal Web
|
||||
The `spring-cloud-cloudfoundry-web` project provides basic support for
|
||||
some enhanced features of webapps in Cloud Foundry: binding
|
||||
automatically to single-sign-on services and optionally enabling
|
||||
sticky routing for discovery.
|
||||
|
||||
The `spring-cloud-cloudfoundry-discovery` project provides an
|
||||
implementation of Spring Cloud Commons `DiscoveryClient` so you can
|
||||
`@EnableDiscoveryClient` and provide your credentials as
|
||||
`spring.cloud.cloudfoundry.discovery.[email,password]` and then you
|
||||
can use the `DiscoveryClient` directly or via a `LoadBalancerClient`
|
||||
(also `*.url` if you are not connecting to [Pivotal Web
|
||||
Services](https://run.pivotal.io)).
|
||||
@@ -9,3 +9,17 @@ the fact that it has to get an access token from Cloud Foundry.
|
||||
|
||||
include::quickstart.adoc[]
|
||||
|
||||
=== Single Sign On
|
||||
|
||||
NOTE: All of the OAuth2 SSO and resource server features moved to Spring Boot
|
||||
in version 1.3. You can find documentation in the
|
||||
http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/[Spring Boot user guide].
|
||||
|
||||
This project provides automatic binding from CloudFoundry service
|
||||
credentials to the Spring Boot features. If you have a CloudFoundry
|
||||
service called "sso", for instance, with credentials containing
|
||||
"client_id", "client_secret" and "auth_domain", it will bind
|
||||
automatically to the Spring OAuth2 client that you enable with
|
||||
`@EnableOAuth2Sso` (from Spring Boot). The name of the service can be
|
||||
parameterized using `spring.oauth2.sso.serviceId`.
|
||||
|
||||
|
||||
@@ -32,8 +32,6 @@ import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
/**
|
||||
* @author <A href= "josh@joshlong.com">Josh Long</A>
|
||||
*/
|
||||
@@ -60,7 +58,7 @@ public class CloudFoundryAutoConfigurationTest {
|
||||
+ "\"state_timestamp\":1431028810}";
|
||||
|
||||
this.context = new SpringApplicationBuilder()
|
||||
.properties(Collections.singletonMap("VCAP_APPLICATION", vcapAppl))
|
||||
.properties("VCAP_APPLICATION:"+vcapAppl, "server.port=0")
|
||||
.sources(SimpleConfiguration.class).run();
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
* 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.cloud.cloudfoundry.environment;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.bind.RelaxedPropertyResolver;
|
||||
import org.springframework.boot.context.config.ConfigFileEnvironmentPostProcessor;
|
||||
import org.springframework.boot.env.EnvironmentPostProcessor;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
import org.springframework.core.env.MapPropertySource;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public class VcapServiceCredentialsEnvironmentPostProcessor
|
||||
implements EnvironmentPostProcessor, Ordered {
|
||||
|
||||
// After VcapEnvironmentPostProcessor and ConfigFileEnvironmentPostProcessor so
|
||||
// values here can
|
||||
// use those ones
|
||||
private int order = ConfigFileEnvironmentPostProcessor.DEFAULT_ORDER + 1;
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return this.order;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postProcessEnvironment(ConfigurableEnvironment environment,
|
||||
SpringApplication application) {
|
||||
RelaxedPropertyResolver resolver = new RelaxedPropertyResolver(environment);
|
||||
Map<String, Object> properties = resolver.getSubProperties("vcap.services.");
|
||||
if (properties == null || properties.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
Map<String, Object> source = new HashMap<String, Object>();
|
||||
String serviceId;
|
||||
if (!resolver.getSubProperties("security.oauth2.resource.").isEmpty()) {
|
||||
serviceId = resolver.getProperty("security.oauth2.resource.serviceId",
|
||||
"resource");
|
||||
}
|
||||
else {
|
||||
serviceId = resolver.getProperty("security.oauth2.sso.serviceId", "sso");
|
||||
}
|
||||
String authDomain = (String) properties
|
||||
.get(serviceId + ".credentials.auth_domain");
|
||||
if (authDomain != null) {
|
||||
source.put("security.oauth2.resource.userInfoUri",
|
||||
authDomain + "/userinfo");
|
||||
source.put("security.oauth2.resource.keyUri", authDomain + "/token_key");
|
||||
source.put("security.oauth2.resource.accessTokenUri",
|
||||
authDomain + "/oauth/token");
|
||||
source.put("security.oauth2.resource.authorizationUri",
|
||||
authDomain + "/oauth/authorization");
|
||||
}
|
||||
else {
|
||||
addProperty(source, resolver, serviceId, "resource", "userInfoUri");
|
||||
addProperty(source, resolver, serviceId, "resource", "tokenInfoUri");
|
||||
addProperty(source, resolver, serviceId, "resource", "keyUri");
|
||||
addProperty(source, resolver, serviceId, "resource", "keyValue");
|
||||
addProperty(source, resolver, serviceId, "client", "accessTokenUri", "tokenUri");
|
||||
addProperty(source, resolver, serviceId, "client", "authorizationUri");
|
||||
}
|
||||
addProperty(source, resolver, serviceId, "client", "clientId");
|
||||
addProperty(source, resolver, serviceId, "client", "clientSecret");
|
||||
addProperty(source, resolver, serviceId, "client", "scope");
|
||||
String resourceId = resolver
|
||||
.getProperty("vcap.services." + serviceId + ".credentials.id", "");
|
||||
if (StringUtils.hasText(resourceId)) {
|
||||
source.put("security.oauth2.resource.id", resourceId);
|
||||
}
|
||||
environment.getPropertySources()
|
||||
.addLast(new MapPropertySource("cloudDefaultSecurityBindings", source));
|
||||
}
|
||||
|
||||
private void addProperty(Map<String, Object> source,
|
||||
RelaxedPropertyResolver resolver, String serviceId, String stem, String key, String... altKeys) {
|
||||
String value = resolve(resolver, serviceId, key);
|
||||
if (StringUtils.hasText(value)) {
|
||||
source.put("security.oauth2."+stem+"." + key, value);
|
||||
return;
|
||||
}
|
||||
for (String altKey : altKeys) {
|
||||
value = resolve(resolver, serviceId, altKey);
|
||||
if (StringUtils.hasText(value)) {
|
||||
source.put("security.oauth2."+stem+"." + key, value);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String resolve(RelaxedPropertyResolver resolver, String serviceId,
|
||||
String key) {
|
||||
return resolver.getProperty(
|
||||
String.format("vcap.services.%s.credentials.%s", serviceId, key),
|
||||
"");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* 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.cloud.cloudfoundry.environment;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.bind.RelaxedPropertyResolver;
|
||||
import org.springframework.boot.test.EnvironmentTestUtils;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
import org.springframework.core.env.StandardEnvironment;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public class VcapServiceCredentialsEnvironmentPostProcessorTests {
|
||||
|
||||
private VcapServiceCredentialsEnvironmentPostProcessor listener = new VcapServiceCredentialsEnvironmentPostProcessor();
|
||||
|
||||
private ConfigurableEnvironment environment = new StandardEnvironment();
|
||||
|
||||
@Test
|
||||
public void noop() {
|
||||
this.listener.postProcessEnvironment(this.environment, new SpringApplication());
|
||||
Map<String, Object> properties = new RelaxedPropertyResolver(this.environment)
|
||||
.getSubProperties("security.oauth2");
|
||||
assertTrue(properties == null || properties.isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void addClientId() {
|
||||
EnvironmentTestUtils.addEnvironment(this.environment,
|
||||
"vcap.services.sso.credentials.clientId:foo");
|
||||
this.listener.postProcessEnvironment(this.environment, new SpringApplication());
|
||||
assertEquals("foo", this.environment
|
||||
.resolvePlaceholders("${security.oauth2.client.clientId}"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void addClientIdUnderscores() {
|
||||
EnvironmentTestUtils.addEnvironment(this.environment,
|
||||
"vcap.services.sso.credentials.client_id:foo");
|
||||
this.listener.postProcessEnvironment(this.environment, new SpringApplication());
|
||||
assertEquals("foo", this.environment
|
||||
.resolvePlaceholders("${security.oauth2.client.clientId}"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void addTokenUri() {
|
||||
EnvironmentTestUtils.addEnvironment(this.environment,
|
||||
"vcap.services.sso.credentials.accessTokenUri:http://example.com");
|
||||
this.listener.postProcessEnvironment(this.environment, new SpringApplication());
|
||||
assertEquals("http://example.com", this.environment
|
||||
.resolvePlaceholders("${security.oauth2.client.accessTokenUri}"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void addUserInfoUri() {
|
||||
EnvironmentTestUtils.addEnvironment(this.environment,
|
||||
"vcap.services.sso.credentials.userInfoUri:http://example.com");
|
||||
this.listener.postProcessEnvironment(this.environment, new SpringApplication());
|
||||
assertEquals("http://example.com", this.environment
|
||||
.resolvePlaceholders("${security.oauth2.resource.userInfoUri}"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void addServiceId() {
|
||||
EnvironmentTestUtils.addEnvironment(this.environment,
|
||||
"vcap.services.my.credentials.tokenUri:http://example.com",
|
||||
"security.oauth2.sso.serviceId:my");
|
||||
this.listener.postProcessEnvironment(this.environment, new SpringApplication());
|
||||
assertEquals("http://example.com", this.environment
|
||||
.resolvePlaceholders("${security.oauth2.client.accessTokenUri}"));
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user