Skip to content

Commit 979bb04

Browse files
Hieu Nguyencbguder
authored andcommitted
Merge branch 'develop'
Release SSO Connector 2.1.1 Signed-off-by: Can Berk Güder <[email protected]>
2 parents 3d64de2 + 4d51c95 commit 979bb04

File tree

9 files changed

+211
-44
lines changed

9 files changed

+211
-44
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ security.oauth2.client.userAuthorizationUri | {ssoServiceUrl}/oauth/authorize
1818
security.oauth2.client.accessTokenUri | {ssoServiceUrl}/oauth/token
1919
security.oauth2.resource.userInfoUri | {ssoServiceUrl}/userinfo
2020
security.oauth2.resource.tokenInfoUri | {ssoServiceUrl}/check_token
21-
security.oauth2.resource.jwt.keyUri | {ssoServiceUrl}/token_key
21+
security.oauth2.resource.jwk.key-set-uri | {ssoServiceUrl}/token_keys
2222

2323
Note: ssoServiceUrl refers to the service uri corresponding to a Pivotal Single Sign-On service plan. For more information on configuring a service plan please refer to http://docs.pivotal.io/p-identity/index.html#create-plan
2424

build.gradle

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,10 +54,12 @@ artifacts {
5454

5555
dependencies {
5656
compile 'org.springframework.cloud:spring-cloud-starter-oauth2'
57+
compile 'org.springframework.security.oauth:spring-security-oauth2:2.2.0.RELEASE'
5758
compile 'org.springframework.cloud:spring-cloud-spring-service-connector'
5859
compile 'org.springframework.cloud:spring-cloud-cloudfoundry-connector'
5960
testCompile group: 'org.springframework.boot', name: 'spring-boot-starter-test'
60-
testCompile group: 'org.springframework.cloud', name: 'spring-cloud-cloudfoundry-connector', version: '1.1.1.RELEASE', classifier: 'tests'
61+
testCompile group: 'org.springframework.cloud', name: 'spring-cloud-cloudfoundry-connector', classifier: 'tests'
62+
testCompile group: 'io.specto', name: 'hoverfly-java', version: '0.8.0'
6163
}
6264

6365
task wrapper(type: Wrapper) {
@@ -83,6 +85,10 @@ uploadArchives {
8385
authentication(userName: ossrhUsername, password: ossrhPassword)
8486
}
8587

88+
pom.whenConfigured { pom ->
89+
pom.dependencies = pom.dependencies.findAll { dep -> dep.scope != 'test' }
90+
}
91+
8692
pom.project {
8793
name 'Spring Cloud SSO Connector'
8894
description 'Spring Cloud Connector Extension for Single Sign-On Service on Pivotal Cloud Foundry'

gradle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
version=2.0.0.RELEASE
1+
version=2.1.1.RELEASE
22

33
ossrhUsername=
44
ossrhPassword=
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package io.pivotal.spring.cloud;
2+
3+
import org.springframework.beans.factory.annotation.Value;
4+
import org.springframework.context.annotation.Bean;
5+
import org.springframework.context.annotation.Configuration;
6+
import org.springframework.security.oauth2.client.discovery.ProviderConfiguration;
7+
import org.springframework.security.oauth2.client.discovery.ProviderDiscoveryClient;
8+
import org.springframework.security.oauth2.provider.token.TokenStore;
9+
import org.springframework.security.oauth2.provider.token.store.IssuerClaimVerifier;
10+
import org.springframework.security.oauth2.provider.token.store.jwk.JwkTokenStore;
11+
12+
import java.net.MalformedURLException;
13+
14+
@Configuration
15+
public class IssuerCheckConfiguration {
16+
@Value("${ssoServiceUrl}")
17+
private String ssoServiceUrl;
18+
19+
@Value("${security.oauth2.resource.jwk.key-set-uri}")
20+
private String keySetUri;
21+
22+
@Bean
23+
public TokenStore jwkTokenStore() throws MalformedURLException {
24+
ProviderDiscoveryClient discoveryClient = new ProviderDiscoveryClient(ssoServiceUrl);
25+
ProviderConfiguration providerConfiguration = discoveryClient.discover();
26+
27+
IssuerClaimVerifier issuerClaimVerifier = new IssuerClaimVerifier(providerConfiguration.getIssuer());
28+
29+
return new JwkTokenStore(
30+
keySetUri,
31+
issuerClaimVerifier
32+
);
33+
}
34+
}

src/main/java/io/pivotal/spring/cloud/SsoServiceCredentialsListener.java

Lines changed: 12 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -6,53 +6,36 @@
66
import org.springframework.cloud.CloudFactory;
77
import org.springframework.cloud.service.ServiceInfo;
88
import org.springframework.context.ApplicationListener;
9-
import org.springframework.context.annotation.Configuration;
109
import org.springframework.core.env.MapPropertySource;
1110

1211
import java.util.HashMap;
1312
import java.util.Map;
1413

15-
@Configuration
1614
public class SsoServiceCredentialsListener implements ApplicationListener<ApplicationEnvironmentPreparedEvent> {
17-
18-
private static final String PROPERTY_SOURCE_NAME = "vcapPivotalSso";
19-
private static final String SPRING_OAUTH2_CLIENT_ID = "security.oauth2.client.clientId";
20-
private static final String SPRING_OAUTH2_CLIENT_SECRET = "security.oauth2.client.clientSecret";
21-
private static final String SPRING_OAUTH2_AUTHORIZE_URI = "security.oauth2.client.userAuthorizationUri";
22-
private static final String SPRING_OAUTH2_KEY_URI = "security.oauth2.resource.jwt.keyUri";
23-
private static final String SPRING_OAUTH2_ACCESS_TOKEN_URI = "security.oauth2.client.accessTokenUri";
24-
private static final String SSO_SERVICE_URL = "ssoServiceUrl";
25-
private static final String SPRING_OAUTH2_USER_INFO_URI = "security.oauth2.resource.userInfoUri";
26-
private static final String SPRING_OAUTH2_TOKEN_INFO_URI = "security.oauth2.resource.tokenInfoUri";
27-
2815
private Cloud cloud;
2916

3017
@Override
3118
public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {
32-
if (cloud != null) {
33-
return;
34-
}
35-
19+
if (cloud != null) return;
3620
try {
3721
cloud = new CloudFactory().getCloud();
3822
} catch (CloudException e) {
39-
// not running on a known cloud environment, so nothing to do
40-
return;
23+
return; // not running on a known cloud environment, so nothing to do
4124
}
4225

4326
for (ServiceInfo serviceInfo : cloud.getServiceInfos()) {
4427
if (serviceInfo instanceof SsoServiceInfo) {
45-
Map<String, Object> map = new HashMap<String, Object>();
28+
Map<String, Object> map = new HashMap<>();
4629
SsoServiceInfo ssoServiceInfo = (SsoServiceInfo) serviceInfo;
47-
map.put(SPRING_OAUTH2_CLIENT_ID, ssoServiceInfo.getClientId());
48-
map.put(SPRING_OAUTH2_CLIENT_SECRET, ssoServiceInfo.getClientSecret());
49-
map.put(SPRING_OAUTH2_ACCESS_TOKEN_URI, ssoServiceInfo.getAuthDomain() + "/oauth/token");
50-
map.put(SPRING_OAUTH2_AUTHORIZE_URI, ssoServiceInfo.getAuthDomain() + "/oauth/authorize");
51-
map.put(SPRING_OAUTH2_KEY_URI, ssoServiceInfo.getAuthDomain() + "/token_key");
52-
map.put(SSO_SERVICE_URL, ssoServiceInfo.getAuthDomain());
53-
map.put(SPRING_OAUTH2_USER_INFO_URI, ssoServiceInfo.getAuthDomain() + "/userinfo");
54-
map.put(SPRING_OAUTH2_TOKEN_INFO_URI, ssoServiceInfo.getAuthDomain() + "/check_token");
55-
MapPropertySource mapPropertySource = new MapPropertySource(PROPERTY_SOURCE_NAME, map);
30+
map.put("security.oauth2.client.clientId", ssoServiceInfo.getClientId());
31+
map.put("security.oauth2.client.clientSecret", ssoServiceInfo.getClientSecret());
32+
map.put("security.oauth2.client.accessTokenUri", ssoServiceInfo.getAuthDomain() + "/oauth/token");
33+
map.put("security.oauth2.client.userAuthorizationUri", ssoServiceInfo.getAuthDomain() + "/oauth/authorize");
34+
map.put("ssoServiceUrl", ssoServiceInfo.getAuthDomain());
35+
map.put("security.oauth2.resource.userInfoUri", ssoServiceInfo.getAuthDomain() + "/userinfo");
36+
map.put("security.oauth2.resource.tokenInfoUri", ssoServiceInfo.getAuthDomain() + "/check_token");
37+
map.put("security.oauth2.resource.jwk.key-set-uri", ssoServiceInfo.getAuthDomain() + "/token_keys");
38+
MapPropertySource mapPropertySource = new MapPropertySource("vcapPivotalSso", map);
5639

5740
event.getEnvironment().getPropertySources().addFirst(mapPropertySource);
5841
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,4 @@
11
org.springframework.context.ApplicationListener=\
22
io.pivotal.spring.cloud.SsoServiceCredentialsListener
3+
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
4+
io.pivotal.spring.cloud.IssuerCheckConfiguration
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package io.pivotal.spring.cloud;
2+
3+
import io.specto.hoverfly.junit.rule.HoverflyRule;
4+
import org.junit.Assert;
5+
import org.junit.ClassRule;
6+
import org.junit.Rule;
7+
import org.junit.Test;
8+
import org.junit.rules.ExpectedException;
9+
import org.junit.runner.RunWith;
10+
import org.springframework.beans.factory.annotation.Autowired;
11+
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
12+
import org.springframework.security.oauth2.common.OAuth2AccessToken;
13+
import org.springframework.security.oauth2.common.exceptions.InvalidTokenException;
14+
import org.springframework.security.oauth2.provider.token.TokenStore;
15+
import org.springframework.test.context.TestPropertySource;
16+
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
17+
18+
@RunWith(SpringJUnit4ClassRunner.class)
19+
@EnableAutoConfiguration
20+
@TestPropertySource(properties = {
21+
"ssoServiceUrl=https://cf-identity-eng-test1.login.run.pivotal.io",
22+
"security.oauth2.resource.jwk.key-set-uri=https://cf-identity-eng-test1.login.run.pivotal.io/token_keys"
23+
})
24+
public class IssuerCheckConfigurationTest {
25+
@ClassRule
26+
public static HoverflyRule hoverflyRule = HoverflyRule.inCaptureOrSimulationMode("IssuerCheckConfigurationTest.json");
27+
28+
@Rule
29+
public ExpectedException thrown = ExpectedException.none();
30+
31+
@Autowired
32+
TokenStore jwkTokenStore;
33+
34+
@Test
35+
public void permitsAValidAccessTokenWithTheCorrectIssuer() {
36+
String accessTokenForZone1 = "eyJhbGciOiJSUzI1NiIsImtpZCI6InNoYTItMjAxNy0wMS0yMC1rZXkiLCJ0eXAiOiJKV1QifQ.eyJqdGkiOiI5MThjZjc0Yzk5NTM0MDhhYjczZTI3YjI4ZGM3NzFmNCIsInN1YiI6ImNmLWlkZW50aXR5LWVuZy10ZXN0MS1jbGllbnQiLCJhdXRob3JpdGllcyI6WyJ1YWEubm9uZSJdLCJzY29wZSI6WyJ1YWEubm9uZSJdLCJjbGllbnRfaWQiOiJjZi1pZGVudGl0eS1lbmctdGVzdDEtY2xpZW50IiwiY2lkIjoiY2YtaWRlbnRpdHktZW5nLXRlc3QxLWNsaWVudCIsImF6cCI6ImNmLWlkZW50aXR5LWVuZy10ZXN0MS1jbGllbnQiLCJncmFudF90eXBlIjoiY2xpZW50X2NyZWRlbnRpYWxzIiwicmV2X3NpZyI6IjVhNzk3NmFlIiwiaWF0IjoxNTAxMTkyODMzLCJleHAiOjE1MDEyMzYwMzMsImlzcyI6Imh0dHBzOi8vY2YtaWRlbnRpdHktZW5nLXRlc3QxLnVhYS5ydW4ucGl2b3RhbC5pby9vYXV0aC90b2tlbiIsInppZCI6ImZiYjYxNjk3LWMwZDctNDg4Zi1hZTFlLTEwMmRiMjk5YjgzMCIsImF1ZCI6WyJjZi1pZGVudGl0eS1lbmctdGVzdDEtY2xpZW50Il19.N6kEA4xXGm9--HAaCA40kaEymH9ON3JSeARU1ZvdvpX_P7E_grq-RFZquAmGvk0_UWBTBlz61pM7HClz7UxKnTJVCLkcCzaokDFjDVhFwRxAiAQ2tdUtEPxxOW7xUoyzIBk6RhdAckOX4kgAj1GXKc4SBHdJed-FXao3VIW5Z1YbpwUTHKnX9q8W5hE_dSIzd6X61r3oIi1WrqHCjkABQys9Osu8VBIjLtYIVut_PR14chZu0swPrVU2L0ncdS28lpYvrJWX_2LbJsoqix2mLcsQ26NBz3skusdJF0buq4Wnb3oz7w-NtWCSLOrhfKDUaIoK3-7f8AXdqWaND7wF0A";
37+
38+
OAuth2AccessToken oAuth2AccessToken = jwkTokenStore.readAccessToken(accessTokenForZone1);
39+
Assert.assertEquals(oAuth2AccessToken.getAdditionalInformation().get("iss"), "https://cf-identity-eng-test1.uaa.run.pivotal.io/oauth/token");
40+
}
41+
42+
@Test
43+
public void rejectsAValidAccessTokenWithADifferentIssuer() {
44+
thrown.expect(InvalidTokenException.class);
45+
thrown.expectMessage("Invalid Issuer (iss) claim: https://cf-identity-eng-test2.uaa.run.pivotal.io/oauth/token");
46+
47+
String accessTokenForZone2 = "eyJhbGciOiJSUzI1NiIsImtpZCI6InNoYTItMjAxNy0wMS0yMC1rZXkiLCJ0eXAiOiJKV1QifQ.eyJqdGkiOiI5ZDQzZGI3ZmE2MGU0MmRmOTA4OWYxYTBkZmZlMGYxMSIsInN1YiI6ImNmLWlkZW50aXR5LWVuZy10ZXN0Mi1jbGllbnQiLCJhdXRob3JpdGllcyI6WyJ1YWEubm9uZSJdLCJzY29wZSI6WyJ1YWEubm9uZSJdLCJjbGllbnRfaWQiOiJjZi1pZGVudGl0eS1lbmctdGVzdDItY2xpZW50IiwiY2lkIjoiY2YtaWRlbnRpdHktZW5nLXRlc3QyLWNsaWVudCIsImF6cCI6ImNmLWlkZW50aXR5LWVuZy10ZXN0Mi1jbGllbnQiLCJncmFudF90eXBlIjoiY2xpZW50X2NyZWRlbnRpYWxzIiwicmV2X3NpZyI6IjFmNTI0ZDAzIiwiaWF0IjoxNTAxMTkyODk1LCJleHAiOjE1MDEyMzYwOTUsImlzcyI6Imh0dHBzOi8vY2YtaWRlbnRpdHktZW5nLXRlc3QyLnVhYS5ydW4ucGl2b3RhbC5pby9vYXV0aC90b2tlbiIsInppZCI6Ijg4NDIxNmJiLWQyNjYtNDA3Mi1iZGIyLWNjZTEyZDc1NDkxNCIsImF1ZCI6WyJjZi1pZGVudGl0eS1lbmctdGVzdDItY2xpZW50Il19.Phmg3vtQOdzhtq3A_G6DOsckwp7u-ANLrXq_DktSyETV-0nmPLrjbAEjxzPROJKuwNrFFMjLCr5ZsbEMa-gV2zXvEKl3B5Wf4S2FZaXnEZmmzkYlQ5PRAYKYzQKJJC6dSicLRAXJdMloktqN94y27BJ8loi9qOGtpSTMZ95Z5M3fjU3fjExN0I8TQwFFZ3a2f_3zmaotb2HEtJtNmdV-wdROMcvO_kmbNVTNCUi-TW9y4gp3VQ8B84c65yXw3tvsmmiF9s8nvcPqyXuSmjkx5XaFRXlctk1D3Aztj2Yj3_tJIPZDIEUicBXCo1UoSSdOQ7iRsiGChmK6vWjdtf7vkg";
48+
jwkTokenStore.readAccessToken(accessTokenForZone2);
49+
}
50+
}

src/test/java/io/pivotal/spring/cloud/SsoServiceCredentialsListenerTest.java

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -17,22 +17,20 @@
1717
import static org.mockito.Mockito.when;
1818

1919
@RunWith(SpringJUnit4ClassRunner.class)
20-
@SpringBootTest(classes = SsoServiceCredentialsListenerTest.TestConfig.class)
20+
@SpringBootTest(classes = SsoServiceCredentialsListener.class)
2121
public class SsoServiceCredentialsListenerTest {
22-
23-
private static SsoServiceInfo info = Mockito.mock(SsoServiceInfo.class);
22+
private static SsoServiceInfo ssoServiceInfo = Mockito.mock(SsoServiceInfo.class);
2423

2524
@Autowired
2625
private Environment environment;
2726

2827
@BeforeClass
2928
public static void beforeClass() {
3029
when(MockCloudConnector.instance.isInMatchingCloud()).thenReturn(true);
31-
when(MockCloudConnector.instance.getServiceInfos()).thenReturn(Collections.singletonList(
32-
info));
33-
when(info.getClientId()).thenReturn("test-client-id");
34-
when(info.getClientSecret()).thenReturn("test-client-secret");
35-
when(info.getAuthDomain()).thenReturn("test-auth-domain");
30+
when(MockCloudConnector.instance.getServiceInfos()).thenReturn(Collections.singletonList(ssoServiceInfo));
31+
when(ssoServiceInfo.getClientId()).thenReturn("test-client-id");
32+
when(ssoServiceInfo.getClientSecret()).thenReturn("test-client-secret");
33+
when(ssoServiceInfo.getAuthDomain()).thenReturn("test-auth-domain");
3634
}
3735

3836
@AfterClass
@@ -46,12 +44,9 @@ public void addClientCredentials() {
4644
assertEquals("test-client-secret", environment.getProperty("security.oauth2.client.clientSecret"));
4745
assertEquals("test-auth-domain/oauth/token", environment.getProperty("security.oauth2.client.accessTokenUri"));
4846
assertEquals("test-auth-domain/oauth/authorize", environment.getProperty("security.oauth2.client.userAuthorizationUri"));
49-
assertEquals("test-auth-domain/token_key", environment.getProperty("security.oauth2.resource.jwt.keyUri"));
47+
assertEquals("test-auth-domain/token_keys", environment.getProperty("security.oauth2.resource.jwk.key-set-uri"));
5048
assertEquals("test-auth-domain/userinfo", environment.getProperty("security.oauth2.resource.userInfoUri"));
5149
assertEquals("test-auth-domain/check_token", environment.getProperty("security.oauth2.resource.tokenInfoUri"));
5250
assertEquals("test-auth-domain", environment.getProperty("ssoServiceUrl"));
5351
}
54-
55-
public static class TestConfig {
56-
}
5752
}

0 commit comments

Comments
 (0)