Implemented confidential client deregistration task and added tests.
Change-Id: I78118166d0660b0816f6da496cead7d46b60b756
diff --git a/core/Changes b/core/Changes
index 00b1efe..592fa84 100644
--- a/core/Changes
+++ b/core/Changes
@@ -1,3 +1,9 @@
+version 0.60.2
+10/04/2018
+ - rearranged and cleaned up codes (margaretha)
+ - generalized some KustvaktException methods (margaretha)
+ - added status codes (margaretha)
+
version 0.60.1
14/03/2018
- removed AdminHandlerIface (margaretha)
diff --git a/core/pom.xml b/core/pom.xml
index 9f94268..cc807bb 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -3,7 +3,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>de.ids_mannheim.korap</groupId>
<artifactId>Kustvakt-core</artifactId>
- <version>0.60.1</version>
+ <version>0.60.2</version>
<properties>
<java.version>1.8</java.version>
diff --git a/full/Changes b/full/Changes
index 16df079..31f8127 100644
--- a/full/Changes
+++ b/full/Changes
@@ -3,7 +3,9 @@
- implemented OAuth2 client registration (margaretha)
- implemented OAuth2 client authentication (margaretha)
- changed virtual corpus search to retrieval (margaretha)
- - implemented deregister public client task (margaretha)
+ - implemented public client deregistration task (margaretha)
+ - added client registration and deregistration tests (margaretha)
+ - implemented confidential client deregistration task (margaretha)
version 0.60.1
28/03/2018
diff --git a/full/src/main/java/de/ids_mannheim/korap/service/OAuth2ClientService.java b/full/src/main/java/de/ids_mannheim/korap/service/OAuth2ClientService.java
index 75d09fb..c6c732b 100644
--- a/full/src/main/java/de/ids_mannheim/korap/service/OAuth2ClientService.java
+++ b/full/src/main/java/de/ids_mannheim/korap/service/OAuth2ClientService.java
@@ -50,6 +50,16 @@
String secret = null;
if (clientJson.getType().equals(OAuth2ClientType.CONFIDENTIAL)) {
+ // RFC 6749:
+ // The authorization server MUST NOT issue client passwords or other
+ // client credentials to native application (clients installed and
+ // executed on the device used by the resource owner e.g. desktop
+ // application, native mobile application) or user-agent-based
+ // application clients for client authentication. The authorization
+ // server MAY issue a client password or other credentials
+ // for a specific installation of a native application client on a
+ // specific device.
+
secret = encryption.createToken();
}
@@ -78,7 +88,7 @@
}
- public void deregisterClient (String clientId, String username)
+ public void deregisterPublicClient (String clientId, String username)
throws KustvaktException {
OAuth2Client client = clientDao.retrieveClientById(clientId);
@@ -88,7 +98,9 @@
else if (client.getType().equals(OAuth2ClientType.CONFIDENTIAL)) {
throw new KustvaktException(
StatusCodes.CLIENT_DEREGISTRATION_FAILED,
- "Service is limited to public clients.");
+ "Service is limited to public clients. To deregister "
+ + "confidential clients, use service at path: "
+ + "oauth2/client/deregister/confidential.");
}
else if (client.getRegisteredBy().equals(username)) {
clientDao.deregisterClient(client);
@@ -100,6 +112,12 @@
}
+ public void deregisterConfidentialClient (String authorization,
+ String clientId) throws KustvaktException {
+ OAuth2Client client = authenticateClient(authorization, null, clientId);
+ clientDao.deregisterClient(client);
+ }
+
public TokenContext requestAccessTokenByClientCredentials (
String authorization, String grantType) throws KustvaktException {
@@ -120,19 +138,19 @@
*
* @param authorization
* @param grantType
- * @param client_id
+ * @param clientId
* @return
* @throws KustvaktException
*/
public OAuth2Client authenticateClient (String authorization,
- GrantType grantType, String client_id) throws KustvaktException {
+ GrantType grantType, String clientId) throws KustvaktException {
- OAuth2Client client = clientDao.retrieveClientById(client_id);
+ OAuth2Client client = clientDao.retrieveClientById(clientId);
if (authorization == null || authorization.isEmpty()) {
if (client.getType().equals(OAuth2ClientType.CONFIDENTIAL)
|| grantType.equals(GrantType.CLIENT_CREDENTIALS)) {
- new KustvaktException(StatusCodes.CLIENT_AUTHENTICATION_FAILED,
+ throw new KustvaktException(StatusCodes.AUTHENTICATION_FAILED,
"Authorization header is not found.");
}
// OAuth2 does not require client authentication
@@ -142,9 +160,11 @@
.parseAuthorizationHeaderValue(authorization);
if (authData.getAuthenticationScheme()
.equals(AuthenticationScheme.BASIC)) {
- if (!client.getSecret().equals(authData.getPassword())) {
- new KustvaktException(
- StatusCodes.CLIENT_AUTHENTICATION_FAILED,
+ authorizationHandler.parseBasicToken(authData);
+ if (!client.getId().equals(clientId)
+ || !client.getSecret().equals(authData.getPassword())) {
+ throw new KustvaktException(
+ StatusCodes.AUTHENTICATION_FAILED,
"Client credentials are incorrect.");
}
}
@@ -158,4 +178,5 @@
}
return client;
}
+
}
diff --git a/full/src/main/java/de/ids_mannheim/korap/web/controller/OAuthClientController.java b/full/src/main/java/de/ids_mannheim/korap/web/controller/OAuthClientController.java
index a770e0f..eba6325 100644
--- a/full/src/main/java/de/ids_mannheim/korap/web/controller/OAuthClientController.java
+++ b/full/src/main/java/de/ids_mannheim/korap/web/controller/OAuthClientController.java
@@ -3,6 +3,7 @@
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.FormParam;
+import javax.ws.rs.HeaderParam;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
@@ -78,13 +79,13 @@
@Path("deregister")
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
@ResourceFilters({ AuthenticationFilter.class, BlockingFilter.class })
- public Response deregisterClient (
+ public Response deregisterPublicClient (
@Context SecurityContext securityContext,
@FormParam("client_id") String clientId) {
TokenContext context =
(TokenContext) securityContext.getUserPrincipal();
try {
- clientService.deregisterClient(clientId,
+ clientService.deregisterPublicClient(clientId,
context.getUsername());
return Response.ok().build();
}
@@ -92,19 +93,21 @@
throw responseHandler.throwit(e);
}
}
-
-// @POST
-// @Path("deregister")
-// public OAuth2ClientDto deregisterClient (
-// @Context SecurityContext securityContext) {
-// TokenContext context =
-// (TokenContext) securityContext.getUserPrincipal();
-// try {
-// return clientService.deregisterClient();
-// }
-// catch (KustvaktException e) {
-// throw responseHandler.throwit(e);
-// }
-// }
+
+ @DELETE
+ @Path("deregister/confidential")
+ @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
+ public Response deregisterConfidentialClient (
+ @Context SecurityContext securityContext,
+ @HeaderParam("Authorization") String authorization,
+ @FormParam("client_id") String clientId) {
+ try {
+ clientService.deregisterConfidentialClient(authorization, clientId);
+ return Response.ok().build();
+ }
+ catch (KustvaktException e) {
+ throw responseHandler.throwit(e);
+ }
+ }
}
diff --git a/full/src/main/java/de/ids_mannheim/korap/web/controller/OAuthController.java b/full/src/main/java/de/ids_mannheim/korap/web/controller/OAuthController.java
index 47e5968..3a91014 100644
--- a/full/src/main/java/de/ids_mannheim/korap/web/controller/OAuthController.java
+++ b/full/src/main/java/de/ids_mannheim/korap/web/controller/OAuthController.java
@@ -43,17 +43,14 @@
import de.ids_mannheim.korap.config.Attributes;
import de.ids_mannheim.korap.config.AuthCodeInfo;
-import de.ids_mannheim.korap.config.BeansFactory;
import de.ids_mannheim.korap.config.ClientInfo;
import de.ids_mannheim.korap.config.KustvaktConfiguration;
import de.ids_mannheim.korap.config.Scopes;
import de.ids_mannheim.korap.constant.AuthenticationMethod;
import de.ids_mannheim.korap.constant.TokenType;
import de.ids_mannheim.korap.exceptions.KustvaktException;
-import de.ids_mannheim.korap.exceptions.StatusCodes;
import de.ids_mannheim.korap.handlers.OAuth2Handler;
import de.ids_mannheim.korap.interfaces.AuthenticationManagerIface;
-import de.ids_mannheim.korap.interfaces.EncryptionIface;
import de.ids_mannheim.korap.security.context.TokenContext;
import de.ids_mannheim.korap.user.User;
import de.ids_mannheim.korap.user.UserDetails;
@@ -97,25 +94,25 @@
}
- @POST
- @Path("unregister")
- @ResourceFilters({ AuthenticationFilter.class, BlockingFilter.class })
- public Response unregisterClient (@Context SecurityContext context,
- @HeaderParam("Host") String host,
- @QueryParam("client_secret") String secret,
- @QueryParam("client_id") String client_id) {
- ClientInfo info = new ClientInfo(client_id, secret);
- info.setUrl(host);
- TokenContext ctx = (TokenContext) context.getUserPrincipal();
- try {
- this.handler.getPersistenceHandler().removeClient(info,
- this.controller.getUser(ctx.getUsername()));
- }
- catch (KustvaktException e) {
- throw kustvaktResponseHandler.throwit(e);
- }
- return Response.ok().build();
- }
+// @POST
+// @Path("unregister")
+// @ResourceFilters({ AuthenticationFilter.class, BlockingFilter.class })
+// public Response unregisterClient (@Context SecurityContext context,
+// @HeaderParam("Host") String host,
+// @QueryParam("client_secret") String secret,
+// @QueryParam("client_id") String client_id) {
+// ClientInfo info = new ClientInfo(client_id, secret);
+// info.setUrl(host);
+// TokenContext ctx = (TokenContext) context.getUserPrincipal();
+// try {
+// this.handler.getPersistenceHandler().removeClient(info,
+// this.controller.getUser(ctx.getUsername()));
+// }
+// catch (KustvaktException e) {
+// throw kustvaktResponseHandler.throwit(e);
+// }
+// return Response.ok().build();
+// }
// @POST
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 dc052ec..e54f354 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
@@ -57,14 +57,19 @@
String entity = response.getEntity(String.class);
assertEquals(Status.OK.getStatusCode(), response.getStatus());
JsonNode node = JsonUtils.readTree(entity);
- assertNotNull(node.at("/client_id").asText());
- assertNotNull(node.at("/client_secret").asText());
+ String clientId = node.at("/client_id").asText();
+ String clientSecret = node.at("/client_secret").asText();
+ assertNotNull(clientId);
+ assertNotNull(clientSecret);
response = testRegisterConfidentialClient();
assertEquals(Status.BAD_REQUEST.getStatusCode(), response.getStatus());
node = JsonUtils.readTree(response.getEntity(String.class));
assertEquals(StatusCodes.CLIENT_REGISTRATION_FAILED,
node.at("/errors/0/0").asInt());
+
+ testDeregisterClientIncorrectCredentials(clientId);
+ testDeregisterConfidentialClient(clientId, clientSecret);
}
@Test
@@ -114,4 +119,49 @@
assertEquals(Status.OK.getStatusCode(), response.getStatus());
}
+
+ 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")
+ .header(Attributes.AUTHORIZATION,
+ handler.createBasicAuthorizationHeaderValue(clientId,
+ clientSecret))
+ .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
+ .header(HttpHeaders.CONTENT_TYPE,
+ ContentType.APPLICATION_FORM_URLENCODED)
+ .entity(form).delete(ClientResponse.class);
+
+ assertEquals(Status.OK.getStatusCode(), response.getStatus());
+ }
+
+ 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")
+ .header(Attributes.AUTHORIZATION,
+ handler.createBasicAuthorizationHeaderValue(clientId,
+ "pass"))
+ .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
+ .header(HttpHeaders.CONTENT_TYPE,
+ ContentType.APPLICATION_FORM_URLENCODED)
+ .entity(form).delete(ClientResponse.class);
+
+ assertEquals(Status.UNAUTHORIZED.getStatusCode(), response.getStatus());
+
+ String entity = response.getEntity(String.class);
+ JsonNode node = JsonUtils.readTree(entity);
+ assertEquals(StatusCodes.AUTHENTICATION_FAILED,
+ node.at("/errors/0/0").asInt());
+ assertEquals("Client credentials are incorrect.",
+ node.at("/errors/0/1").asText());
+ }
}