Implemented authorization code request, simplified client
authentication, and added tests.
Change-Id: Id6695cacc6da75da64588499ea3a7c7b1ad64591
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 66d610f..b194e08 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
@@ -62,6 +62,7 @@
json.setType(OAuth2ClientType.CONFIDENTIAL);
json.setUrl("http://example.client.com");
json.setRedirectURI("https://example.client.com/redirect");
+ json.setDescription("This is a confidential test client.");
return resource().path("oauth2").path("client").path("register")
.header(Attributes.AUTHORIZATION,
@@ -89,8 +90,9 @@
assertEquals(OAuthError.TokenResponse.INVALID_REQUEST,
node.at("/error").asText());
+ testDeregisterConfidentialClientMissingParameters();
testDeregisterClientIncorrectCredentials(clientId);
- testDeregisterConfidentialClient(clientId, clientSecret);
+ testDeregisterConfidentialClient(clientId,clientSecret);
}
@Test
@@ -101,6 +103,7 @@
json.setType(OAuth2ClientType.PUBLIC);
json.setUrl("http://test.public.client.com");
json.setRedirectURI("https://test.public.client.com/redirect");
+ json.setDescription("This is a public test client.");
ClientResponse response = resource().path("oauth2").path("client")
.path("register")
@@ -129,6 +132,7 @@
json.setType(OAuth2ClientType.PUBLIC);
json.setUrl("http://korap.ids-mannheim.de/native");
json.setRedirectURI("https://korap.ids-mannheim.de/native/redirect");
+ json.setDescription("This is a native test client.");
ClientResponse response = resource().path("oauth2").path("client")
.path("register")
@@ -140,7 +144,7 @@
.entity(json).post(ClientResponse.class);
assertEquals(Status.OK.getStatusCode(), response.getStatus());
-
+
//EM: need to check native
}
@@ -167,8 +171,6 @@
private void testDeregisterConfidentialClient (String clientId,
String clientSecret) throws UniformInterfaceException,
ClientHandlerException, KustvaktException {
- MultivaluedMap<String, String> form = new MultivaluedMapImpl();
- form.add("client_id", clientId);
ClientResponse response = resource().path("oauth2").path("client")
.path("deregister").path("confidential")
@@ -178,16 +180,34 @@
.header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
.header(HttpHeaders.CONTENT_TYPE,
ContentType.APPLICATION_FORM_URLENCODED)
- .entity(form).delete(ClientResponse.class);
+ .delete(ClientResponse.class);
assertEquals(Status.OK.getStatusCode(), response.getStatus());
}
+ private void testDeregisterConfidentialClientMissingParameters ()
+ throws KustvaktException {
+
+ ClientResponse response = resource().path("oauth2").path("client")
+ .path("deregister").path("confidential")
+ .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
+ .header(HttpHeaders.CONTENT_TYPE,
+ ContentType.APPLICATION_FORM_URLENCODED)
+ .delete(ClientResponse.class);
+
+ String entity = response.getEntity(String.class);
+ assertEquals(Status.BAD_REQUEST.getStatusCode(), response.getStatus());
+
+ JsonNode node = JsonUtils.readTree(entity);
+ assertEquals(OAuthError.TokenResponse.INVALID_REQUEST,
+ node.at("/error").asText());
+ assertEquals("Missing parameters: client_secret client_id",
+ node.at("/error_description").asText());
+ }
+
private void testDeregisterClientIncorrectCredentials (String clientId)
throws UniformInterfaceException, ClientHandlerException,
KustvaktException {
- MultivaluedMap<String, String> form = new MultivaluedMapImpl();
- form.add("client_id", clientId);
ClientResponse response = resource().path("oauth2").path("client")
.path("deregister").path("confidential")
@@ -197,7 +217,7 @@
.header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
.header(HttpHeaders.CONTENT_TYPE,
ContentType.APPLICATION_FORM_URLENCODED)
- .entity(form).delete(ClientResponse.class);
+ .delete(ClientResponse.class);
String entity = response.getEntity(String.class);
assertEquals(Status.UNAUTHORIZED.getStatusCode(), response.getStatus());
@@ -205,7 +225,7 @@
JsonNode node = JsonUtils.readTree(entity);
assertEquals(OAuthError.TokenResponse.INVALID_CLIENT,
node.at("/error").asText());
- assertEquals("Invalid client credentials.",
+ assertEquals("Invalid client credentials",
node.at("/error_description").asText());
checkWWWAuthenticateHeader(response);
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 956e3ed..3214fda 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
@@ -2,6 +2,9 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.net.URI;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response.Status;
@@ -14,9 +17,7 @@
import com.fasterxml.jackson.databind.JsonNode;
import com.google.common.net.HttpHeaders;
-import com.sun.jersey.api.client.ClientHandlerException;
import com.sun.jersey.api.client.ClientResponse;
-import com.sun.jersey.api.client.UniformInterfaceException;
import com.sun.jersey.core.util.MultivaluedMapImpl;
import de.ids_mannheim.korap.authentication.http.HttpAuthorizationHandler;
@@ -34,24 +35,99 @@
@Autowired
private HttpAuthorizationHandler handler;
- private ClientResponse testRequestTokenConfidentialClient (
- MultivaluedMap<String, String> form)
- throws UniformInterfaceException, ClientHandlerException,
- KustvaktException {
- return resource().path("oauth2").path("token")
+ private ClientResponse requestAuthorizationConfidentialClient (
+ MultivaluedMap<String, String> form) throws KustvaktException {
+
+ return resource().path("oauth2").path("authorize")
.header(Attributes.AUTHORIZATION,
handler.createBasicAuthorizationHeaderValue(
- "fCBbQkAyYzI4NzUxMg==", "secret"))
+ "fCBbQkAyYzI4NzUxMg", "secret"))
.header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
.header(HttpHeaders.CONTENT_TYPE,
ContentType.APPLICATION_FORM_URLENCODED)
.entity(form).post(ClientResponse.class);
}
- private ClientResponse testRequestTokenPublicClient (
- MultivaluedMap<String, String> form)
- throws UniformInterfaceException, ClientHandlerException,
- KustvaktException {
+ @Test
+ public void testAuthorizeConfidentialClient () throws KustvaktException {
+ MultivaluedMap<String, String> form = new MultivaluedMapImpl();
+ form.add("response_type", "code");
+ form.add("client_id", "fCBbQkAyYzI4NzUxMg");
+ form.add("username", "dory");
+ form.add("password", "password");
+ ClientResponse response = requestAuthorizationConfidentialClient(form);
+
+ assertEquals(Status.TEMPORARY_REDIRECT.getStatusCode(),
+ response.getStatus());
+ URI redirectUri = response.getLocation();
+ assertTrue(redirectUri.getQuery().startsWith("code"));
+ }
+
+ @Test
+ public void testAuthorizeInvalidRedirectUri () throws KustvaktException {
+ String redirectUri = "https://different.uri/redirect";
+
+ MultivaluedMap<String, String> form = new MultivaluedMapImpl();
+ form.add("response_type", "code");
+ form.add("client_id", "fCBbQkAyYzI4NzUxMg");
+ form.add("username", "dory");
+ form.add("password", "password");
+ form.add("redirect_uri", redirectUri);
+ ClientResponse response = requestAuthorizationConfidentialClient(form);
+
+ assertEquals(Status.BAD_REQUEST.getStatusCode(), response.getStatus());
+
+ String entity = response.getEntity(String.class);
+ JsonNode node = JsonUtils.readTree(entity);
+ assertEquals(OAuthError.CodeResponse.INVALID_REQUEST,
+ node.at("/error").asText());
+ assertEquals(redirectUri + " is unknown",
+ node.at("/error_description").asText());
+ }
+
+ @Test
+ public void testAuthorizeMissingRequiredParameters ()
+ throws KustvaktException {
+ MultivaluedMap<String, String> form = new MultivaluedMapImpl();
+ // missing code
+ ClientResponse response = requestAuthorizationConfidentialClient(form);
+
+ assertEquals(Status.BAD_REQUEST.getStatusCode(), response.getStatus());
+
+ String entity = response.getEntity(String.class);
+ JsonNode node = JsonUtils.readTree(entity);
+ assertEquals(OAuthError.CodeResponse.INVALID_REQUEST,
+ node.at("/error").asText());
+ assertEquals("Missing response_type parameter value",
+ node.at("/error_description").asText());
+
+ // missing client_id
+ form.add("response_type", "code");
+ response = requestAuthorizationConfidentialClient(form);
+ entity = response.getEntity(String.class);
+ node = JsonUtils.readTree(entity);
+ assertEquals("Missing parameters: client_id",
+ node.at("/error_description").asText());
+ }
+
+ @Test
+ public void testAuthorizeInvalidResponseType () throws KustvaktException {
+ MultivaluedMap<String, String> form = new MultivaluedMapImpl();
+ form.add("response_type", "string");
+
+ ClientResponse response = requestAuthorizationConfidentialClient(form);
+ assertEquals(Status.BAD_REQUEST.getStatusCode(), response.getStatus());
+
+ String entity = response.getEntity(String.class);
+ JsonNode node = JsonUtils.readTree(entity);
+ assertEquals(OAuthError.CodeResponse.INVALID_REQUEST,
+ node.at("/error").asText());
+ assertEquals("Invalid response_type parameter value",
+ node.at("/error_description").asText());
+ }
+
+ private ClientResponse requestToken (MultivaluedMap<String, String> form)
+ throws KustvaktException {
return resource().path("oauth2").path("token")
.header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
.header(HttpHeaders.CONTENT_TYPE,
@@ -61,12 +137,15 @@
@Test
public void testRequestTokenPasswordGrantConfidential ()
- throws UniformInterfaceException, ClientHandlerException,
- KustvaktException {
+ throws KustvaktException {
MultivaluedMap<String, String> form = new MultivaluedMapImpl();
form.add("grant_type", "password");
+ form.add("client_id", "fCBbQkAyYzI4NzUxMg");
+ form.add("client_secret", "secret");
+ form.add("username", "dory");
+ form.add("password", "password");
- ClientResponse response = testRequestTokenConfidentialClient(form);
+ ClientResponse response = requestToken(form);
String entity = response.getEntity(String.class);
JsonNode node = JsonUtils.readTree(entity);
@@ -78,71 +157,80 @@
}
@Test
- public void testRequestTokenConfidentialMissingSecret ()
- throws UniformInterfaceException, ClientHandlerException,
- KustvaktException {
+ public void testRequestTokenPasswordGrantMissingClientSecret ()
+ throws KustvaktException {
MultivaluedMap<String, String> form = new MultivaluedMapImpl();
form.add("grant_type", "password");
- form.add("client_id", "fCBbQkAyYzI4NzUxMg==");
+ form.add("username", "dory");
+ form.add("password", "password");
+ form.add("client_id", "fCBbQkAyYzI4NzUxMg");
- ClientResponse response = testRequestTokenPublicClient(form);
- assertEquals(Status.UNAUTHORIZED.getStatusCode(), response.getStatus());
+ ClientResponse response = requestToken(form);
+ assertEquals(Status.BAD_REQUEST.getStatusCode(), response.getStatus());
String entity = response.getEntity(String.class);
JsonNode node = JsonUtils.readTree(entity);
- assertEquals(OAuthError.TokenResponse.INVALID_CLIENT,
+ assertEquals(OAuthError.TokenResponse.INVALID_REQUEST,
node.at("/error").asText());
- }
-
- @Test
- public void testRequestTokenPasswordGrantPublic ()
- throws UniformInterfaceException, ClientHandlerException,
- KustvaktException {
- MultivaluedMap<String, String> form = new MultivaluedMapImpl();
- form.add("grant_type", "password");
- form.add("client_id", "iBr3LsTCxOj7D2o0A5m");
-
- ClientResponse response = testRequestTokenPublicClient(form);
- String entity = response.getEntity(String.class);
-
- JsonNode node = JsonUtils.readTree(entity);
- assertNotNull(node.at("/access_token").asText());
- assertNotNull(node.at("/refresh_token").asText());
- assertEquals(TokenType.BEARER.toString(),
- node.at("/token_type").asText());
- assertNotNull(node.at("/expires_in").asText());
+ assertEquals("Missing parameters: client_secret",
+ node.at("/error_description").asText());
}
@Test
public void testRequestTokenPasswordGrantMissingClientId ()
- throws UniformInterfaceException, ClientHandlerException,
- KustvaktException {
+ throws KustvaktException {
MultivaluedMap<String, String> form = new MultivaluedMapImpl();
form.add("grant_type", "password");
+ form.add("username", "dory");
+ form.add("password", "password");
+ form.add("client_secret", "secret");
- ClientResponse response = testRequestTokenPublicClient(form);
+ ClientResponse response = requestToken(form);
String entity = response.getEntity(String.class);
assertEquals(Status.BAD_REQUEST.getStatusCode(), response.getStatus());
JsonNode node = JsonUtils.readTree(entity);
assertEquals(OAuthError.TokenResponse.INVALID_REQUEST,
node.at("/error").asText());
- assertEquals("client_id is missing",
+ assertEquals("Missing parameters: client_id",
node.at("/error_description").asText());
}
+
+ @Test
+ public void testRequestTokenPasswordGrantPublic ()
+ throws KustvaktException {
+ MultivaluedMap<String, String> form = new MultivaluedMapImpl();
+ form.add("grant_type", "password");
+ form.add("username", "dory");
+ form.add("password", "password");
+ form.add("client_id", "iBr3LsTCxOj7D2o0A5m");
+
+ ClientResponse response = requestToken(form);
+ String entity = response.getEntity(String.class);
+
+ JsonNode node = JsonUtils.readTree(entity);
+ assertNotNull(node.at("/access_token").asText());
+ assertNotNull(node.at("/refresh_token").asText());
+ assertEquals(TokenType.BEARER.toString(),
+ node.at("/token_type").asText());
+ assertNotNull(node.at("/expires_in").asText());
+ }
@Test
public void testRequestTokenPasswordGrantNonNative ()
- throws UniformInterfaceException, ClientHandlerException,
- KustvaktException {
+ throws KustvaktException {
MultivaluedMap<String, String> form = new MultivaluedMapImpl();
form.add("grant_type", "password");
- form.add("client_id", "8bIDtZnH6NvRkW2Fq==");
+ form.add("username", "dory");
+ form.add("password", "password");
+ // confidential nonnative
+ form.add("client_id", "9aHsGW6QflV13ixNpez");
+ form.add("client_secret", "secret");
- ClientResponse response = testRequestTokenPublicClient(form);
+ ClientResponse response = requestToken(form);
String entity = response.getEntity(String.class);
- assertEquals(Status.BAD_REQUEST.getStatusCode(), response.getStatus());
+ assertEquals(Status.UNAUTHORIZED.getStatusCode(), response.getStatus());
JsonNode node = JsonUtils.readTree(entity);
assertEquals(OAuthError.TokenResponse.UNAUTHORIZED_CLIENT,
@@ -153,13 +241,13 @@
@Test
public void testRequestTokenClientCredentialsGrant ()
- throws UniformInterfaceException, ClientHandlerException,
- KustvaktException {
+ throws KustvaktException {
MultivaluedMap<String, String> form = new MultivaluedMapImpl();
form.add("grant_type", "client_credentials");
-
- ClientResponse response = testRequestTokenConfidentialClient(form);
+ form.add("client_id", "fCBbQkAyYzI4NzUxMg");
+ form.add("client_secret", "secret");
+ ClientResponse response = requestToken(form);
String entity = response.getEntity(String.class);
assertEquals(Status.OK.getStatusCode(), response.getStatus());
@@ -173,11 +261,9 @@
}
@Test
- public void testRequestTokenMissingGrantType ()
- throws UniformInterfaceException, ClientHandlerException,
- KustvaktException {
+ public void testRequestTokenMissingGrantType () throws KustvaktException {
MultivaluedMap<String, String> form = new MultivaluedMapImpl();
- ClientResponse response = testRequestTokenConfidentialClient(form);
+ ClientResponse response = requestToken(form);
assertEquals(Status.BAD_REQUEST.getStatusCode(), response.getStatus());
String entity = response.getEntity(String.class);
@@ -187,9 +273,7 @@
}
@Test
- public void testRequestTokenUnsupportedGrant ()
- throws UniformInterfaceException, ClientHandlerException,
- KustvaktException {
+ public void testRequestTokenUnsupportedGrant () throws KustvaktException {
MultivaluedMap<String, String> form = new MultivaluedMapImpl();
form.add("grant_type", "blahblah");
diff --git a/full/src/test/resources/test-config.xml b/full/src/test/resources/test-config.xml
index 0445d65..c0f7a2e 100644
--- a/full/src/test/resources/test-config.xml
+++ b/full/src/test/resources/test-config.xml
@@ -167,10 +167,10 @@
<!-- URLValidator -->
<bean id="urlValidator" class="org.apache.commons.validator.routines.UrlValidator">
- <constructor-arg value="http,https"/>
+ <constructor-arg value="http,https" />
</bean>
<bean id="httpsValidator" class="org.apache.commons.validator.routines.UrlValidator">
- <constructor-arg value="https"/>
+ <constructor-arg value="https" />
</bean>
<bean id="kustvakt_rewrite" class="de.ids_mannheim.korap.rewrite.FullRewriteHandler">
@@ -184,10 +184,18 @@
<bean id="kustvaktExceptionHandler" class="de.ids_mannheim.korap.web.KustvaktExceptionHandler">
<constructor-arg index="0" name="iface" ref="kustvakt_auditing" />
</bean>
+
+ <!-- OAuth -->
<bean id="oauth2ResponseHandler" class="de.ids_mannheim.korap.web.OAuth2ResponseHandler">
<constructor-arg index="0" name="iface" ref="kustvakt_auditing" />
</bean>
+ <bean id="mdGenerator" class="org.apache.oltu.oauth2.as.issuer.MD5Generator">
+ </bean>
+ <bean id="oauthIssuer" class="org.apache.oltu.oauth2.as.issuer.OAuthIssuerImpl">
+ <constructor-arg index="0" ref="mdGenerator" />
+ </bean>
+
<bean id="kustvakt_userdb" class="de.ids_mannheim.korap.handlers.EntityDao">
<constructor-arg ref="kustvakt_db" />
</bean>
@@ -200,8 +208,7 @@
<constructor-arg ref="kustvakt_db" />
</bean>
- <bean name="kustvakt_encryption"
- class="de.ids_mannheim.korap.encryption.KustvaktEncryption">
+ <bean name="kustvakt_encryption" class="de.ids_mannheim.korap.encryption.KustvaktEncryption">
<constructor-arg ref="kustvakt_config" />
</bean>
@@ -267,8 +274,8 @@
<!-- specify type for constructor argument -->
<bean id="kustvakt_authenticationmanager"
class="de.ids_mannheim.korap.authentication.KustvaktAuthenticationManager">
- <constructor-arg
- type="de.ids_mannheim.korap.interfaces.EntityHandlerIface" ref="kustvakt_userdb" />
+ <constructor-arg type="de.ids_mannheim.korap.interfaces.EntityHandlerIface"
+ ref="kustvakt_userdb" />
<constructor-arg type="de.ids_mannheim.korap.interfaces.EncryptionIface"
ref="kustvakt_encryption" />
<constructor-arg ref="kustvakt_config" />
@@ -309,8 +316,8 @@
<!-- mail -->
<bean id="authenticator" class="de.ids_mannheim.korap.service.MailAuthenticator">
- <constructor-arg index="0" value="${mail.username}"/>
- <constructor-arg index="1" value="${mail.password}"/>
+ <constructor-arg index="0" value="${mail.username}" />
+ <constructor-arg index="1" value="${mail.password}" />
</bean>
<bean id="smtpSession" class="javax.mail.Session" factory-method="getInstance">
<constructor-arg index="0">
@@ -323,7 +330,7 @@
<prop key="mail.smtp.connectiontimeout">${mail.connectiontimeout}</prop>
</props>
</constructor-arg>
- <constructor-arg index="1" ref="authenticator"/>
+ <constructor-arg index="1" ref="authenticator" />
</bean>
<bean id="mailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl">
<property name="session" ref="smtpSession" />