diff --git a/build.gradle b/build.gradle index 9af72ef88..009694fa3 100644 --- a/build.gradle +++ b/build.gradle @@ -67,7 +67,7 @@ configure(allprojects) { dependencies { testCompile("org.springframework:spring-test:${springVersion}") - testCompile("junit:junit-dep:${junitVersion}") + testCompile("junit:junit:${junitVersion}") testCompile("org.hamcrest:hamcrest-library:${hamcrestVersion}") testCompile("org.mockito:mockito-core:${mockitoVersion}") } diff --git a/gradle.properties b/gradle.properties index 0d1e4a26e..152d71ff6 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,15 +1,15 @@ -version=1.1.3.BUILD-SNAPSHOT -h2Version=1.3.175 -springSecurityVersion=3.2.8.RELEASE -junitVersion=4.11 -httpComponentsVersion=4.3.1 -springVersion=4.1.7.RELEASE +h2Version=1.4.190 +springSecurityVersion=4.0.3.RELEASE +junitVersion=4.12 +httpComponentsVersion=4.5.1 +springVersion=4.1.8.RELEASE springSnapshotVersion=latest.integration hamcrestVersion=1.3 +version=1.1.3.BUILD-SNAPSHOT jacksonVersion=2.6.1 -servletApiVersion=3.0.1 +servletApiVersion=3.1.0 springReleaseVersion=latest.release jspApiVersion=2.2.1 -mockitoVersion=1.10.8 +mockitoVersion=1.10.19 javaxInjectVersion=1 thymeleafVersion=2.1.4.RELEASE diff --git a/spring-social-core/src/main/java/org/springframework/social/support/ClientHttpRequestFactorySelector.java b/spring-social-core/src/main/java/org/springframework/social/support/ClientHttpRequestFactorySelector.java index 1180e3922..b094b73b6 100644 --- a/spring-social-core/src/main/java/org/springframework/social/support/ClientHttpRequestFactorySelector.java +++ b/spring-social-core/src/main/java/org/springframework/social/support/ClientHttpRequestFactorySelector.java @@ -18,24 +18,29 @@ import java.net.InetSocketAddress; import java.net.Proxy; import java.net.URI; -import java.security.KeyManagementException; -import java.security.KeyStore; -import java.security.KeyStoreException; -import java.security.NoSuchAlgorithmException; +import java.security.GeneralSecurityException; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.util.Properties; +import javax.net.ssl.HostnameVerifier; import javax.net.ssl.SSLContext; import org.apache.http.HttpHost; import org.apache.http.client.protocol.HttpClientContext; +import org.apache.http.config.Registry; +import org.apache.http.config.RegistryBuilder; +import org.apache.http.conn.socket.ConnectionSocketFactory; +import org.apache.http.conn.socket.PlainConnectionSocketFactory; +import org.apache.http.conn.ssl.NoopHostnameVerifier; import org.apache.http.conn.ssl.SSLConnectionSocketFactory; -import org.apache.http.conn.ssl.SSLContexts; import org.apache.http.conn.ssl.TrustStrategy; import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.client.HttpClients; +import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.apache.http.protocol.HttpContext; +import org.apache.http.ssl.SSLContextBuilder; import org.springframework.http.HttpMethod; import org.springframework.http.client.BufferingClientHttpRequestFactory; import org.springframework.http.client.ClientHttpRequestFactory; @@ -114,31 +119,31 @@ private static CloseableHttpClient getClient(HttpHost proxy) { } private static CloseableHttpClient getAllTrustClient(HttpHost proxy) { - return HttpClients.custom() - .setProxy(proxy) - .setSslcontext(getSSLContext()) - .setHostnameVerifier(SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER) - .build(); - } - - private static SSLContext getSSLContext() { try { - KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType()); - TrustStrategy allTrust = new TrustStrategy() { - @Override + HttpClientBuilder clientBuilder = HttpClientBuilder.create(); + SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() { public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException { return true; } - }; - return SSLContexts.custom().useSSL().loadTrustMaterial(trustStore, allTrust).build(); - } catch (KeyStoreException e) { - e.printStackTrace(); - } catch (KeyManagementException e) { - e.printStackTrace(); - } catch (NoSuchAlgorithmException e) { - e.printStackTrace(); + }).build(); + clientBuilder.setSSLContext(sslContext); + + HostnameVerifier hostnameVerifier = new NoopHostnameVerifier(); + + SSLConnectionSocketFactory sslSocketFactory = new SSLConnectionSocketFactory(sslContext, hostnameVerifier); + Registry socketFactoryRegistry = RegistryBuilder.create() + .register("http", PlainConnectionSocketFactory.getSocketFactory()) + .register("https", sslSocketFactory) + .build(); + + PoolingHttpClientConnectionManager connMgr = new PoolingHttpClientConnectionManager( socketFactoryRegistry); + clientBuilder.setConnectionManager(connMgr); + + return clientBuilder.build(); + } catch (GeneralSecurityException e) { + // shouldn't happen + throw new RuntimeException(e); } - return null; } } diff --git a/spring-social-web/src/main/java/org/springframework/social/connect/web/ConnectSupport.java b/spring-social-web/src/main/java/org/springframework/social/connect/web/ConnectSupport.java index 982b1a41e..e8ae3c5aa 100644 --- a/spring-social-web/src/main/java/org/springframework/social/connect/web/ConnectSupport.java +++ b/spring-social-web/src/main/java/org/springframework/social/connect/web/ConnectSupport.java @@ -169,8 +169,8 @@ public Connection completeConnection(OAuth2ConnectionFactory connectionFac private void verifyStateParameter(NativeWebRequest request) { String state = request.getParameter("state"); String originalState = extractCachedOAuth2State(request); - if (state != null && !state.equals(originalState)) { - throw new IllegalStateException("The OAuth2 'state' parameter doesn't match."); + if (state == null || !state.equals(originalState)) { + throw new IllegalStateException("The OAuth2 'state' parameter is missing or doesn't match."); } } diff --git a/spring-social-web/src/test/java/org/springframework/social/connect/web/ConnectControllerTest.java b/spring-social-web/src/test/java/org/springframework/social/connect/web/ConnectControllerTest.java index 57f7765c4..436340d8e 100644 --- a/spring-social-web/src/test/java/org/springframework/social/connect/web/ConnectControllerTest.java +++ b/spring-social-web/src/test/java/org/springframework/social/connect/web/ConnectControllerTest.java @@ -21,6 +21,7 @@ import static org.springframework.social.connect.web.test.StubOAuthTemplateBehavior.*; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.request; import static org.springframework.test.web.servlet.setup.MockMvcBuilders.*; import java.util.ArrayList; @@ -291,9 +292,10 @@ public void oauth2Callback() throws Exception { List> interceptors = getConnectInterceptor(); connectController.setConnectInterceptors(interceptors); connectController.afterPropertiesSet(); - MockMvc mockMvc = standaloneSetup(connectController).build(); + MockMvc mockMvc = standaloneSetup(connectController) + .build(); assertEquals(0, connectionRepository.findConnections("oauth2Provider").size()); - mockMvc.perform(get("/connect/oauth2Provider").param("code", "oauth2Code")) + mockMvc.perform(get("/connect/oauth2Provider").param("code", "oauth2Code").param("state", "STATE").sessionAttr("oauth2State", "STATE")) .andExpect(redirectedUrl("/connect/oauth2Provider")); List> connections = connectionRepository.findConnections("oauth2Provider"); assertEquals(1, connections.size()); diff --git a/spring-social-web/src/test/java/org/springframework/social/connect/web/ConnectSupportTest.java b/spring-social-web/src/test/java/org/springframework/social/connect/web/ConnectSupportTest.java index 01f619ce2..6a9b0a495 100644 --- a/spring-social-web/src/test/java/org/springframework/social/connect/web/ConnectSupportTest.java +++ b/spring-social-web/src/test/java/org/springframework/social/connect/web/ConnectSupportTest.java @@ -354,9 +354,11 @@ public void completeConnection_OAuth2() { ConnectSupport support = new ConnectSupport(); MockHttpServletRequest mockRequest = new PortAwareMockHttpServletRequest(); mockRequest.addParameter("code", "authorization-grant"); + mockRequest.addParameter("state", "STATE"); mockRequest.setScheme("http"); mockRequest.setServerName("somesite.com"); mockRequest.setRequestURI("/connect/someprovider"); + mockRequest.getSession().setAttribute("oauth2State", "STATE"); ServletWebRequest request = new ServletWebRequest(mockRequest); Connection connection = support.completeConnection(new TestOAuth2ConnectionFactory(), request); assertEquals("TestUser", connection.getDisplayName()); @@ -466,6 +468,11 @@ public TestOAuth2ConnectionFactory() { super("someprovider", SERVICE_PROVIDER, API_ADAPTER); } +// @Override +// public boolean supportsStateParameter() { +// return false; +// } + @Override public Connection createConnection(AccessGrant accessGrant) { return new OAuth2Connection("someprovider", "providerUserId", accessGrant.getAccessToken(), diff --git a/spring-social-web/src/test/java/org/springframework/social/connect/web/ProviderSignInControllerTest.java b/spring-social-web/src/test/java/org/springframework/social/connect/web/ProviderSignInControllerTest.java index fc934e40f..09ea9daa9 100644 --- a/spring-social-web/src/test/java/org/springframework/social/connect/web/ProviderSignInControllerTest.java +++ b/spring-social-web/src/test/java/org/springframework/social/connect/web/ProviderSignInControllerTest.java @@ -19,6 +19,7 @@ import static org.hamcrest.Matchers.*; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.request; import static org.springframework.test.web.servlet.setup.MockMvcBuilders.*; import java.util.Arrays; @@ -227,7 +228,8 @@ public void oauth2Callback_noMatchingUser() throws Exception { ProviderSignInController providerSignInController = new ProviderSignInController(connectionFactoryLocator, usersConnectionRepository, null); providerSignInController.afterPropertiesSet(); MockMvc mockMvc = standaloneSetup(providerSignInController).build(); - mockMvc.perform(get("/signin/oauth2Provider").param("code", "authcode")) + mockMvc.perform(get("/signin/oauth2Provider").param("code", "authcode").param("state", "STATE") + .sessionAttr("oauth2State", "STATE")) .andExpect(redirectedUrl("/signup")) .andExpect(request().sessionAttribute(ProviderSignInAttempt.class.getName(), notNullValue())); // TODO: Assert attempt contents @@ -243,7 +245,8 @@ public void oauth2Callback_noMatchingUser_customSignUpUrl() throws Exception { controller.setSignUpUrl("/register"); controller.afterPropertiesSet(); MockMvc mockMvc = standaloneSetup(controller).build(); - mockMvc.perform(get("/signin/oauth2Provider").param("code", "authcode")) + mockMvc.perform(get("/signin/oauth2Provider").param("code", "authcode").param("state", "STATE") + .sessionAttr("oauth2State", "STATE")) .andExpect(redirectedUrl("/register")) .andExpect(request().sessionAttribute(ProviderSignInAttempt.class.getName(), notNullValue())); // TODO: Assert attempt contents @@ -286,7 +289,8 @@ public void oauth2Callback_multipleMatchingUsers() throws Exception { ProviderSignInController providerSignInController = new ProviderSignInController(connectionFactoryLocator, usersConnectionRepository, null); providerSignInController.afterPropertiesSet(); MockMvc mockMvc = standaloneSetup(providerSignInController).build(); - mockMvc.perform(get("/signin/oauth2Provider").param("code", "authcode")) + mockMvc.perform(get("/signin/oauth2Provider").param("code", "authcode").param("state", "STATE") + .sessionAttr("oauth2State", "STATE")) .andExpect(redirectedUrl("/signin?error=multiple_users")); } @@ -300,7 +304,8 @@ public void oauth2Callback_multipleMatchingUsers_customSignInUrl() throws Except controller.afterPropertiesSet(); controller.setSignInUrl("/customsignin?someparameter=1234"); MockMvc mockMvc = standaloneSetup(controller).build(); - mockMvc.perform(get("/signin/oauth2Provider").param("code", "authcode")) + mockMvc.perform(get("/signin/oauth2Provider").param("code", "authcode").param("state", "STATE") + .sessionAttr("oauth2State", "STATE")) .andExpect(redirectedUrl("/customsignin?someparameter=1234&error=multiple_users")); } @@ -342,7 +347,8 @@ private void performOAuth2Callback(String originalUrl, String postSignInUrl) thr controller.setPostSignInUrl(postSignInUrl); } MockMvc mockMvc = standaloneSetup(controller).build(); - mockMvc.perform(get("/signin/oauth2Provider").param("code", "authcode")) + mockMvc.perform(get("/signin/oauth2Provider").param("code", "authcode").param("state", "STATE") + .sessionAttr("oauth2State", "STATE")) .andExpect(redirectedUrl(calculateExpectedRedirectUrl(originalUrl, postSignInUrl))); // TODO: Verify that the connection is updated (connectionRepository.updateConnection() is called) }