Allowed OAuth2 clients to use localhost as redirect URIs.
Change-Id: Ia57668ab77dcdd68220d998c89bbcf366699409c
diff --git a/full/Changes b/full/Changes
index 4dac86d..8147bcc 100644
--- a/full/Changes
+++ b/full/Changes
@@ -7,6 +7,8 @@
client authentication)
2023-01-30
- Made scope param required in authorization request (solved #508)
+2023-01-31
+- Allowed OAuth2 clients to use localhost as redirect URIs.
# version 0.69.1
diff --git a/full/src/main/resources/default-config.xml b/full/src/main/resources/default-config.xml
index 86f686e..53c0536 100644
--- a/full/src/main/resources/default-config.xml
+++ b/full/src/main/resources/default-config.xml
@@ -209,10 +209,14 @@
<!-- URLValidator -->
<bean id="redirectURIValidator" class="org.apache.commons.validator.routines.UrlValidator">
<constructor-arg value="http,https" index="0" />
- <constructor-arg index="1" type="long">
+ <constructor-arg index="1" type="long"
+ value="#{T(org.apache.commons.validator.routines.UrlValidator).ALLOW_LOCAL_URLS +
+ T(org.apache.commons.validator.routines.UrlValidator).NO_FRAGMENTS}"/>
+
+ <!-- <constructor-arg index="1" type="long">
<util:constant
static-field="org.apache.commons.validator.routines.UrlValidator.NO_FRAGMENTS" />
- </constructor-arg>
+ </constructor-arg> -->
</bean>
<bean id="urlValidator" class="org.apache.commons.validator.routines.UrlValidator">
<constructor-arg value="http,https" />
diff --git a/full/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2ClientControllerTest.java b/full/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2ClientControllerTest.java
index a0655c6..880d8f2 100644
--- a/full/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2ClientControllerTest.java
+++ b/full/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2ClientControllerTest.java
@@ -12,22 +12,20 @@
import java.util.Map.Entry;
import java.util.Set;
+import javax.ws.rs.ProcessingException;
+import javax.ws.rs.client.Entity;
import javax.ws.rs.core.Form;
-import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
import org.apache.commons.io.IOUtils;
import org.apache.http.entity.ContentType;
import org.apache.oltu.oauth2.common.error.OAuthError;
+import org.glassfish.jersey.server.ContainerRequest;
import org.junit.Test;
import com.fasterxml.jackson.databind.JsonNode;
import com.google.common.net.HttpHeaders;
-import javax.ws.rs.ProcessingException;
-import javax.ws.rs.core.Response;
-import javax.ws.rs.core.Response.Status;
-import javax.ws.rs.client.Entity;
-
-import org.glassfish.jersey.server.ContainerRequest;
import de.ids_mannheim.korap.authentication.http.HttpAuthorizationHandler;
import de.ids_mannheim.korap.config.Attributes;
@@ -223,11 +221,11 @@
response.getStatus());
// localhost is not allowed
- redirectUri = "http://localhost:1410";
- clientJson.setRedirectURI(redirectUri);
- response = registerClient(username, clientJson);
- testInvalidRedirectUri(response.readEntity(String.class), false,
- response.getStatus());
+// redirectUri = "http://localhost:1410";
+// clientJson.setRedirectURI(redirectUri);
+// response = registerClient(username, clientJson);
+// testInvalidRedirectUri(response.readEntity(String.class), false,
+// response.getStatus());
// fragment is not allowed
redirectUri = "https://public.client.com/redirect.html#bar";
diff --git a/full/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2ControllerTest.java b/full/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2ControllerTest.java
index 1a6d89f..f31596f 100644
--- a/full/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2ControllerTest.java
+++ b/full/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2ControllerTest.java
@@ -182,11 +182,18 @@
@Test
public void testAuthorizeWithRedirectUriLocalhost ()
throws KustvaktException {
- Response response =
- requestAuthorizationCode("code", publicClientId2,
- "http://localhost:1410", "search", state, userAuthHeader);
- testInvalidRedirectUri(response.readEntity(String.class), true,
- response.getStatus()); }
+ Response response = requestAuthorizationCode("code", publicClientId2,
+ "http://localhost:1410", "search", state, userAuthHeader);
+ assertEquals(Status.TEMPORARY_REDIRECT.getStatusCode(),
+ response.getStatus());
+
+ URI redirectUri = response.getLocation();
+ MultivaluedMap<String, String> params =
+ getQueryParamsFromURI(redirectUri);
+ assertNotNull(params.getFirst("code"));
+ assertEquals(state, params.getFirst("state"));
+ assertEquals("search", params.getFirst("scope"));
+ }
@Test
public void testAuthorizeWithRedirectUriFragment ()
diff --git a/full/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2RClientTest.java b/full/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2RClientTest.java
new file mode 100644
index 0000000..358cf18
--- /dev/null
+++ b/full/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2RClientTest.java
@@ -0,0 +1,107 @@
+package de.ids_mannheim.korap.web.controller;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import java.io.IOException;
+import java.net.URI;
+
+import javax.ws.rs.ProcessingException;
+import javax.ws.rs.client.Client;
+import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.client.ClientProperties;
+import org.junit.Test;
+import org.springframework.util.MultiValueMap;
+import org.springframework.web.util.UriComponentsBuilder;
+
+import com.fasterxml.jackson.databind.JsonNode;
+
+import de.ids_mannheim.korap.authentication.http.HttpAuthorizationHandler;
+import de.ids_mannheim.korap.config.Attributes;
+import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.oauth2.constant.OAuth2ClientType;
+import de.ids_mannheim.korap.utils.JsonUtils;
+import de.ids_mannheim.korap.web.input.OAuth2ClientJson;
+
+public class OAuth2RClientTest extends OAuth2TestBase {
+
+ private String username = "OAuth2ClientControllerTest";
+ private String userAuthHeader;
+
+ public OAuth2RClientTest () throws KustvaktException {
+ userAuthHeader = HttpAuthorizationHandler
+ .createBasicAuthorizationHeaderValue("R-user", "password");
+ }
+
+ public OAuth2ClientJson createOAuth2RClient () {
+ OAuth2ClientJson client = new OAuth2ClientJson();
+ client.setName("R client");
+ client.setType(OAuth2ClientType.PUBLIC);
+ client.setDescription("An R client with httr web server.");
+ client.setRedirectURI("http://localhost:1410");
+ return client;
+ }
+
+ @Test
+ public void testRClientWithLocalhost ()
+ throws ProcessingException, KustvaktException, IOException {
+ // Register client
+ OAuth2ClientJson clientJson = createOAuth2RClient();
+ Response response = registerClient(username, clientJson);
+
+ assertEquals(Status.OK.getStatusCode(), response.getStatus());
+
+ JsonNode node = JsonUtils.readTree(response.readEntity(String.class));
+ String clientId = node.at("/client_id").asText();
+
+ // send authorization
+ String code = testAuthorize(clientId);
+
+ // send token request
+ response =
+ requestTokenWithAuthorizationCodeAndForm(clientId, null, code);
+
+ assertEquals(Status.OK.getStatusCode(),
+ response.getStatus());
+
+ String entity = response.readEntity(String.class);
+ node = JsonUtils.readTree(entity);
+
+ // testing
+ String accessToken = node.at("/access_token").asText();
+ testSearchWithOAuth2Token(accessToken);
+
+ // cleaning up
+ deregisterClient(username, clientId);
+
+ testSearchWithRevokedAccessToken(accessToken);
+ }
+
+ private String testAuthorize (String clientId) throws KustvaktException {
+
+ Response response = requestAuthorizationCode("code", clientId, "",
+ "search", "", userAuthHeader);
+
+ assertEquals(Status.TEMPORARY_REDIRECT.getStatusCode(),
+ response.getStatus());
+
+ URI redirectUri = response.getLocation();
+
+ assertEquals("http", redirectUri.getScheme());
+ assertEquals("localhost", redirectUri.getHost());
+ assertEquals(1410, redirectUri.getPort());
+
+ MultiValueMap<String, String> params = UriComponentsBuilder
+ .fromUri(redirectUri).build().getQueryParams();
+ String code = params.getFirst("code");
+ assertNotNull(code);
+ assertEquals("search", params.getFirst("scope"));
+ return code;
+ }
+
+}
diff --git a/full/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2TestBase.java b/full/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2TestBase.java
index a84f8f7..48d888c 100644
--- a/full/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2TestBase.java
+++ b/full/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2TestBase.java
@@ -8,6 +8,8 @@
import java.net.URI;
import javax.ws.rs.ProcessingException;
+import javax.ws.rs.client.Client;
+import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.Entity;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.Form;
@@ -18,6 +20,8 @@
import org.apache.http.entity.ContentType;
import org.apache.oltu.oauth2.common.error.OAuthError;
import org.apache.oltu.oauth2.common.message.types.GrantType;
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.client.ClientProperties;
import org.glassfish.jersey.uri.UriComponent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.MultiValueMap;
@@ -82,9 +86,13 @@
String clientId, String redirectUri, String scope, String state,
String authHeader) throws KustvaktException {
- WebTarget request =
- target().path(API_VERSION).path("oauth2").path("authorize");
+ ClientConfig clientConfig = new ClientConfig();
+ clientConfig.property(ClientProperties.FOLLOW_REDIRECTS, false);
+ Client client = ClientBuilder.newClient(clientConfig);
+ WebTarget request = client.target(getBaseUri()).path(API_VERSION)
+ .path("oauth2").path("authorize");
+
if (!responseType.isEmpty()) {
request = request.queryParam("response_type", responseType);
}
diff --git a/full/src/test/resources/test-config.xml b/full/src/test/resources/test-config.xml
index dbcd8b9..aaea39c 100644
--- a/full/src/test/resources/test-config.xml
+++ b/full/src/test/resources/test-config.xml
@@ -191,10 +191,9 @@
<!-- URLValidator -->
<bean id="redirectURIValidator" class="org.apache.commons.validator.routines.UrlValidator">
<constructor-arg value="http,https" index="0" />
- <constructor-arg index="1" type="long">
- <util:constant
- static-field="org.apache.commons.validator.routines.UrlValidator.NO_FRAGMENTS" />
- </constructor-arg>
+ <constructor-arg index="1" type="long"
+ value="#{T(org.apache.commons.validator.routines.UrlValidator).ALLOW_LOCAL_URLS +
+ T(org.apache.commons.validator.routines.UrlValidator).NO_FRAGMENTS}"/>
</bean>
<bean id="urlValidator" class="org.apache.commons.validator.routines.UrlValidator">
<constructor-arg value="http,https" />