diff --git a/openid-connect-client/pom.xml b/openid-connect-client/pom.xml index b891d4e0e6..847838aa6f 100644 --- a/openid-connect-client/pom.xml +++ b/openid-connect-client/pom.xml @@ -1,76 +1,83 @@ - - - - - 4.0.0 - - openid-connect-parent - org.mitre - 1.3.4-SNAPSHOT - .. - - openid-connect-client - OpenID Connect Client filter for Spring Security - OpenID Connect Client - - - org.mitre - openid-connect-common - - - jar - - - - org.apache.maven.plugins - maven-compiler-plugin - - ${java-version} - ${java-version} - - - - - org.apache.maven.plugins - maven-source-plugin - - - attach-sources - - jar-no-fork - - - - - - - org.apache.maven.plugins - maven-javadoc-plugin - - - attach-sources - - jar - - - - - - - + + + + + 4.0.0 + + openid-connect-parent + org.mitre + 1.3.4-SNAPSHOT + .. + + openid-connect-client + OpenID Connect Client filter for Spring Security + OpenID Connect Client + + + org.mitre + openid-connect-common + + + + com.fasterxml.jackson.dataformat + jackson-dataformat-xml + 2.9.9 + test + + + jar + + + + org.apache.maven.plugins + maven-compiler-plugin + + ${java-version} + ${java-version} + + + + + org.apache.maven.plugins + maven-source-plugin + + + attach-sources + + jar-no-fork + + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + + + attach-sources + + jar + + + + + + + diff --git a/openid-connect-client/src/main/java/org/mitre/oauth2/introspectingfilter/IntrospectingTokenService.java b/openid-connect-client/src/main/java/org/mitre/oauth2/introspectingfilter/IntrospectingTokenService.java index b311a84d9c..4e67f0f242 100644 --- a/openid-connect-client/src/main/java/org/mitre/oauth2/introspectingfilter/IntrospectingTokenService.java +++ b/openid-connect-client/src/main/java/org/mitre/oauth2/introspectingfilter/IntrospectingTokenService.java @@ -22,6 +22,7 @@ import java.io.IOException; import java.net.URI; import java.util.Calendar; +import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.HashSet; @@ -36,7 +37,11 @@ import org.mitre.oauth2.model.RegisteredClient; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; import org.springframework.http.client.ClientHttpRequest; import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; import org.springframework.security.core.Authentication; @@ -309,7 +314,12 @@ protected ClientHttpRequest createRequest(URI url, HttpMethod method) throws IOE form.add("token", accessToken); try { - validatedToken = restTemplate.postForObject(introspectionUrl, form, String.class); + + HttpHeaders headers = new HttpHeaders(); + headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON)); + + ResponseEntity response = restTemplate.exchange(introspectionUrl, HttpMethod.POST, new HttpEntity<>(form, headers), String.class); + validatedToken = response.getBody(); } catch (RestClientException rce) { logger.error("validateToken", rce); return null; diff --git a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/DynamicServerConfigurationService.java b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/DynamicServerConfigurationService.java index 5f451c2dcb..f67212dcce 100644 --- a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/DynamicServerConfigurationService.java +++ b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/DynamicServerConfigurationService.java @@ -27,6 +27,7 @@ import static org.mitre.util.JsonUtils.getAsString; import static org.mitre.util.JsonUtils.getAsStringList; +import java.util.Collections; import java.util.HashSet; import java.util.Set; import java.util.concurrent.ExecutionException; @@ -37,6 +38,11 @@ import org.mitre.openid.connect.config.ServerConfiguration; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; import org.springframework.security.authentication.AuthenticationServiceException; import org.springframework.web.client.RestTemplate; @@ -141,7 +147,9 @@ private class OpenIDConnectServiceConfigurationFetcher extends CacheLoader response = restTemplate.exchange(url, HttpMethod.GET, new HttpEntity<>(headers), String.class); + String jsonString = response.getBody(); JsonElement parsed = parser.parse(jsonString); if (parsed.isJsonObject()) { diff --git a/openid-connect-client/src/test/java/org/mitre/oauth2/introspectingfilter/TestIntrospectingTokenService.java b/openid-connect-client/src/test/java/org/mitre/oauth2/introspectingfilter/TestIntrospectingTokenService.java new file mode 100644 index 0000000000..6c80cf4517 --- /dev/null +++ b/openid-connect-client/src/test/java/org/mitre/oauth2/introspectingfilter/TestIntrospectingTokenService.java @@ -0,0 +1,132 @@ +package org.mitre.oauth2.introspectingfilter; + +import org.apache.http.Header; +import org.apache.http.HttpResponse; +import org.apache.http.StatusLine; +import org.apache.http.client.ClientProtocolException; +import org.apache.http.client.HttpClient; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.entity.StringEntity; +import org.apache.http.protocol.HttpContext; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mitre.oauth2.introspectingfilter.service.IntrospectionConfigurationService; +import org.mitre.oauth2.introspectingfilter.service.impl.JWTParsingIntrospectionConfigurationService; +import org.mitre.oauth2.introspectingfilter.service.impl.StaticIntrospectionConfigurationService; +import org.mitre.oauth2.model.RegisteredClient; +import org.mitre.openid.connect.client.service.impl.DynamicServerConfigurationService; +import org.mitre.openid.connect.config.ServerConfiguration; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.runners.MockitoJUnitRunner; +import org.springframework.http.MediaType; +import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; +import org.springframework.security.oauth2.provider.OAuth2Authentication; +import org.springframework.web.client.RestTemplate; + +import java.io.IOException; +import java.util.List; + +import static org.hamcrest.CoreMatchers.*; +import static org.junit.Assert.assertThat; + +/** + * @author Loic Gangloff + * + */ +@RunWith(MockitoJUnitRunner.class) +public class TestIntrospectingTokenService { + + + private IntrospectingTokenService service; + + private String introspectionUrl = "http://www.example.com"; + private RegisteredClient client = new RegisteredClient(); + + private String json = "{\"active\":true,\"scope\":\"sub openid profile\",\"exp\":1591344840,\"sub\":\"test@example.com\",\"username\":\"test@example.com\",\"expires_at\":\"2020-06-05T10:14:00+0200\",\"client_id\":\"a-client-id\",\"token_type\":\"Bearer\"}"; + + private String xml = "" + + "" + + "true" + + "a-client-id" + + "1591344840n" + + "2020-06-05T10:14:00+0200" + + "sub openid profile" + + "test@example.com" + + "Bearer" + + "test@example.com" + + ""; + + @Mock + private HttpClient mockHttpClient; + + @Before + public void prepare() { + client.setClientId("a-client-id"); + client.setClientSecret("a-client-secret"); + + StaticIntrospectionConfigurationService introspectionConfig = new StaticIntrospectionConfigurationService(); + introspectionConfig.setClientConfiguration(client); + introspectionConfig.setIntrospectionUrl(introspectionUrl); + + service = new IntrospectingTokenService(mockHttpClient); + service.setIntrospectionConfigurationService(introspectionConfig); + } + + + @Test + public void resttemplate_must_have_xml_accept_header() throws IOException { + + HttpResponse response = Mockito.mock(HttpResponse.class); + StatusLine statusLine = Mockito.mock(StatusLine.class); + Mockito.when(statusLine.getStatusCode()).thenReturn(200); + Mockito.when(response.getAllHeaders()).thenReturn(new Header[] {}); + Mockito.when(response.getStatusLine()).thenReturn(statusLine); + Mockito.when(response.getEntity()).thenReturn(new StringEntity(json)); + Mockito.when(mockHttpClient.execute(Mockito.any(), (HttpContext) Mockito.any())).thenReturn(response); + + RestTemplate restemplate = Mockito.spy(new RestTemplate(new HttpComponentsClientHttpRequestFactory(mockHttpClient))); + + restemplate.postForObject(introspectionUrl, "a-token", String.class); + + ArgumentCaptor argument = ArgumentCaptor.forClass(HttpPost.class); + Mockito.verify(mockHttpClient).execute(argument.capture(), (HttpContext) Mockito.any()); + + + Header acceptHeader = argument.getValue().getFirstHeader("Accept"); + List mediaTypes = MediaType.parseMediaTypes(acceptHeader.getValue()); + + assertThat(mediaTypes, hasItem(MediaType.APPLICATION_JSON)); + assertThat(mediaTypes, hasItem(MediaType.APPLICATION_XML)); + + } + + @Test + public void introspectingtokenservice_must_not_have_xml_accept_header() throws IOException { + + HttpResponse response = Mockito.mock(HttpResponse.class); + StatusLine statusLine = Mockito.mock(StatusLine.class); + Mockito.when(statusLine.getStatusCode()).thenReturn(200); + Mockito.when(response.getAllHeaders()).thenReturn(new Header[] {}); + Mockito.when(response.getStatusLine()).thenReturn(statusLine); + Mockito.when(response.getEntity()).thenReturn(new StringEntity(json)); + Mockito.when(mockHttpClient.execute(Mockito.any(), (HttpContext) Mockito.any())).thenReturn(response); + + OAuth2Authentication result = service.loadAuthentication("a-token"); + + ArgumentCaptor argument = ArgumentCaptor.forClass(HttpPost.class); + Mockito.verify(mockHttpClient).execute(argument.capture(), (HttpContext) Mockito.any()); + + + Header acceptHeader = argument.getValue().getFirstHeader("Accept"); + List mediaTypes = MediaType.parseMediaTypes(acceptHeader.getValue()); + + assertThat(mediaTypes, hasItem(MediaType.APPLICATION_JSON)); + assertThat(mediaTypes, not(hasItem(MediaType.APPLICATION_XML))); + + } + +} diff --git a/openid-connect-client/src/test/java/org/mitre/openid/connect/client/service/impl/TestDynamicServerConfigurationService.java b/openid-connect-client/src/test/java/org/mitre/openid/connect/client/service/impl/TestDynamicServerConfigurationService.java new file mode 100644 index 0000000000..a6dd1bcd8d --- /dev/null +++ b/openid-connect-client/src/test/java/org/mitre/openid/connect/client/service/impl/TestDynamicServerConfigurationService.java @@ -0,0 +1,114 @@ +package org.mitre.openid.connect.client.service.impl; + +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.hasItem; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.not; +import static org.junit.Assert.assertThat; + +import java.io.IOException; +import java.util.List; + +import org.apache.http.Header; +import org.apache.http.HttpResponse; +import org.apache.http.StatusLine; +import org.apache.http.client.ClientProtocolException; +import org.apache.http.client.HttpClient; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.entity.StringEntity; +import org.apache.http.protocol.HttpContext; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mitre.openid.connect.config.ServerConfiguration; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.runners.MockitoJUnitRunner; +import org.springframework.http.MediaType; +import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; +import org.springframework.web.client.RestTemplate; + +/** + * @author Loic Gangloff + * + */ +@RunWith(MockitoJUnitRunner.class) +public class TestDynamicServerConfigurationService { + + + private DynamicServerConfigurationService service; + + private String issuer = "https://www.example.com/"; + + + private String json = "{\"issuer\": \"https://www.example.com/\"}"; + + private String xml = "" + + "" + + "https://www.example.com/" + + ""; + + @Mock + private HttpClient mockHttpClient; + + @Before + public void prepare() { + service = new DynamicServerConfigurationService(mockHttpClient); + } + + + @Test + public void resttemplate_must_have_xml_accept_header() throws ClientProtocolException, IOException { + + HttpResponse response = Mockito.mock(HttpResponse.class); + StatusLine statusLine = Mockito.mock(StatusLine.class); + Mockito.when(statusLine.getStatusCode()).thenReturn(200); + Mockito.when(response.getAllHeaders()).thenReturn(new Header[] {}); + Mockito.when(response.getStatusLine()).thenReturn(statusLine); + Mockito.when(response.getEntity()).thenReturn(new StringEntity(json)); + Mockito.when(mockHttpClient.execute(Mockito.any(), (HttpContext) Mockito.any())).thenReturn(response); + + RestTemplate restemplate = Mockito.spy(new RestTemplate(new HttpComponentsClientHttpRequestFactory(mockHttpClient))); + + restemplate.getForObject(issuer, String.class); + + ArgumentCaptor argument = ArgumentCaptor.forClass(HttpGet.class); + Mockito.verify(mockHttpClient).execute(argument.capture(), (HttpContext) Mockito.any()); + + + Header acceptHeader = argument.getValue().getFirstHeader("Accept"); + List mediaTypes = MediaType.parseMediaTypes(acceptHeader.getValue()); + + assertThat(mediaTypes, hasItem(MediaType.APPLICATION_JSON)); + assertThat(mediaTypes, hasItem(MediaType.APPLICATION_XML)); + + } + + @Test + public void serverconfiguration_must_not_have_xml_accept_header() throws ClientProtocolException, IOException { + + HttpResponse response = Mockito.mock(HttpResponse.class); + StatusLine statusLine = Mockito.mock(StatusLine.class); + Mockito.when(statusLine.getStatusCode()).thenReturn(200); + Mockito.when(response.getAllHeaders()).thenReturn(new Header[] {}); + Mockito.when(response.getStatusLine()).thenReturn(statusLine); + Mockito.when(response.getEntity()).thenReturn(new StringEntity(json)); + Mockito.when(mockHttpClient.execute(Mockito.any(), (HttpContext) Mockito.any())).thenReturn(response); + + ServerConfiguration result = service.getServerConfiguration(issuer); + + ArgumentCaptor argument = ArgumentCaptor.forClass(HttpGet.class); + Mockito.verify(mockHttpClient).execute(argument.capture(), (HttpContext) Mockito.any()); + + + Header acceptHeader = argument.getValue().getFirstHeader("Accept"); + List mediaTypes = MediaType.parseMediaTypes(acceptHeader.getValue()); + + assertThat(mediaTypes, hasItem(MediaType.APPLICATION_JSON)); + assertThat(mediaTypes, not(hasItem(MediaType.APPLICATION_XML))); + + assertThat(result.getIssuer(), is(equalTo(issuer))); + } + +}