Implemented a service to list clients registered by a user (close #52)
Change-Id: I3b828ec8e7a50573b52bfb1fec59f3430f9be785
diff --git a/full/Changes b/full/Changes
index 257ee98..372efbe 100644
--- a/full/Changes
+++ b/full/Changes
@@ -16,6 +16,8 @@
14/11/2019
- Added client description and URL to list-authorized-clients service
(margaretha, close #53)
+ - Implemented a service to list clients registered by a user (margaretha,
+ close #52)
# version 0.62.1
08/07/2019
diff --git a/full/src/main/java/de/ids_mannheim/korap/oauth2/dao/OAuth2ClientDao.java b/full/src/main/java/de/ids_mannheim/korap/oauth2/dao/OAuth2ClientDao.java
index 896d77b..ef44bfa 100644
--- a/full/src/main/java/de/ids_mannheim/korap/oauth2/dao/OAuth2ClientDao.java
+++ b/full/src/main/java/de/ids_mannheim/korap/oauth2/dao/OAuth2ClientDao.java
@@ -107,7 +107,7 @@
client = entityManager.merge(client);
}
- public List<OAuth2Client> retrieveUserClients (String username)
+ public List<OAuth2Client> retrieveUserAuthorizedClients (String username)
throws KustvaktException {
ParameterChecker.checkStringValue(username, "username");
@@ -133,4 +133,21 @@
return q.getResultList();
}
+ public List<OAuth2Client> retrieveUserRegisteredClients (String username)
+ throws KustvaktException {
+ ParameterChecker.checkStringValue(username, "username");
+
+ CriteriaBuilder builder = entityManager.getCriteriaBuilder();
+ CriteriaQuery<OAuth2Client> query =
+ builder.createQuery(OAuth2Client.class);
+
+ Root<OAuth2Client> client = query.from(OAuth2Client.class);
+ query.select(client);
+ query.where(builder.equal(client.get(OAuth2Client_.registeredBy),
+ username));
+ query.distinct(true);
+ TypedQuery<OAuth2Client> q = entityManager.createQuery(query);
+ return q.getResultList();
+ }
+
}
diff --git a/full/src/main/java/de/ids_mannheim/korap/oauth2/service/OAuth2ClientService.java b/full/src/main/java/de/ids_mannheim/korap/oauth2/service/OAuth2ClientService.java
index 9bf6259..de7a90c 100644
--- a/full/src/main/java/de/ids_mannheim/korap/oauth2/service/OAuth2ClientService.java
+++ b/full/src/main/java/de/ids_mannheim/korap/oauth2/service/OAuth2ClientService.java
@@ -349,18 +349,35 @@
return clientDao.retrieveClientById(clientId);
}
- public List<OAuth2UserClientDto> listUserClients (String username,
+ public List<OAuth2UserClientDto> listUserAuthorizedClients (String username,
String clientId, String clientSecret) throws KustvaktException {
OAuth2Client client = authenticateClient(clientId, clientSecret);
if (!client.isSuper()) {
throw new KustvaktException(StatusCodes.CLIENT_AUTHORIZATION_FAILED,
- "Only super client is allowed to list user clients.",
+ "Only super client is allowed to list user authorized clients.",
OAuth2Error.UNAUTHORIZED_CLIENT);
}
List<OAuth2Client> userClients =
- clientDao.retrieveUserClients(username);
+ clientDao.retrieveUserAuthorizedClients(username);
Collections.sort(userClients);
-
+ return createClientDtos(userClients);
+ }
+
+ public List<OAuth2UserClientDto> listUserRegisteredClients (String username,
+ String clientId, String clientSecret) throws KustvaktException {
+ OAuth2Client client = authenticateClient(clientId, clientSecret);
+ if (!client.isSuper()) {
+ throw new KustvaktException(StatusCodes.CLIENT_AUTHORIZATION_FAILED,
+ "Only super client is allowed to list user registered clients.",
+ OAuth2Error.UNAUTHORIZED_CLIENT);
+ }
+ List<OAuth2Client> userClients =
+ clientDao.retrieveUserRegisteredClients(username);
+ Collections.sort(userClients);
+ return createClientDtos(userClients);
+ }
+
+ private List<OAuth2UserClientDto> createClientDtos (List<OAuth2Client> userClients) {
List<OAuth2UserClientDto> dtoList = new ArrayList<>(userClients.size());
for (OAuth2Client uc : userClients) {
if (uc.isSuper()) continue;
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 69cb4e0..be4cb9f 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
@@ -125,7 +125,7 @@
@DELETE
@Path("deregister/{client_id}")
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
- public Response deregisterPublicClient (
+ public Response deregisterClient (
@Context SecurityContext securityContext,
@PathParam("client_id") String clientId,
@FormParam("client_secret") String clientSecret) {
@@ -264,7 +264,7 @@
scopeService.verifyScope(tokenContext,
OAuth2Scope.LIST_USER_CLIENT);
- return clientService.listUserClients(username, clientId,
+ return clientService.listUserAuthorizedClients(username, clientId,
clientSecret);
}
catch (KustvaktException e) {
@@ -272,4 +272,42 @@
}
}
+ /**
+ * Lists clients registered by the authenticated user, e.g. an R
+ * client. This service is intended for client management. It is
+ * not part of the OAuth2 specification. Only super clients are
+ * allowed to use this service. It requires user and client
+ * authentications.
+ *
+ * @param context
+ * @param clientId
+ * the client id of the super client
+ * @param clientSecret
+ * the client secret of the super client
+ * @return a list of clients registered by a user
+ */
+ @POST
+ @Path("registered")
+ @ResourceFilters({ AuthenticationFilter.class, BlockingFilter.class })
+ @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
+ @Produces(MediaType.APPLICATION_JSON + ";charset=utf-8")
+ public List<OAuth2UserClientDto> listUserRegisteredClients (
+ @Context SecurityContext context,
+ @FormParam("client_id") String clientId,
+ @FormParam("client_secret") String clientSecret) {
+
+ TokenContext tokenContext = (TokenContext) context.getUserPrincipal();
+ String username = tokenContext.getUsername();
+
+ try {
+ scopeService.verifyScope(tokenContext,
+ OAuth2Scope.LIST_USER_CLIENT);
+
+ return clientService.listUserRegisteredClients(username, clientId,
+ clientSecret);
+ }
+ catch (KustvaktException e) {
+ throw responseHandler.throwit(e);
+ }
+ }
}
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 827684e..dfbcee8 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
@@ -56,6 +56,18 @@
}
}
}
+
+ private ClientResponse registerClient (String username,
+ OAuth2ClientJson json) throws UniformInterfaceException,
+ ClientHandlerException, KustvaktException {
+ return resource().path(API_VERSION).path("oauth2").path("client")
+ .path("register")
+ .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
+ .createBasicAuthorizationHeaderValue(username, "password"))
+ .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
+ .header(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON)
+ .entity(json).post(ClientResponse.class);
+ }
private ClientResponse registerConfidentialClient ()
throws KustvaktException {
@@ -67,13 +79,7 @@
json.setRedirectURI("https://example.client.com/redirect");
json.setDescription("This is a confidential test client.");
- return resource().path(API_VERSION).path("oauth2").path("client")
- .path("register")
- .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
- .createBasicAuthorizationHeaderValue(username, "pass"))
- .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
- .header(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON)
- .entity(json).post(ClientResponse.class);
+ return registerClient(username, json);
}
private JsonNode retrieveClientInfo (String clientId, String username)
@@ -156,13 +162,7 @@
json.setRedirectURI("https://test.public.client.com/redirect");
json.setDescription("This is a public test client.");
- ClientResponse response = resource().path(API_VERSION).path("oauth2")
- .path("client").path("register")
- .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
- .createBasicAuthorizationHeaderValue(username, "pass"))
- .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
- .header(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON)
- .entity(json).post(ClientResponse.class);
+ ClientResponse response = registerClient(username, json);
String entity = response.getEntity(String.class);
assertEquals(Status.OK.getStatusCode(), response.getStatus());
@@ -183,13 +183,7 @@
json.setType(OAuth2ClientType.PUBLIC);
json.setDescription("This is a desktop test client.");
- ClientResponse response = resource().path(API_VERSION).path("oauth2")
- .path("client").path("register")
- .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
- .createBasicAuthorizationHeaderValue(username, "pass"))
- .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
- .header(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON)
- .entity(json).post(ClientResponse.class);
+ ClientResponse response = registerClient(username, json);
String entity = response.getEntity(String.class);
assertEquals(Status.OK.getStatusCode(), response.getStatus());
@@ -200,7 +194,7 @@
testDeregisterPublicClientMissingUserAuthentication(clientId);
testDeregisterPublicClientMissingId();
- testDeregisterPublicClient(clientId);
+ testDeregisterPublicClient(clientId,username);
}
private void testAccessTokenAfterDeregistration (String clientId,
@@ -219,7 +213,7 @@
assertEquals(Status.OK.getStatusCode(), response.getStatus());
code = requestAuthorizationCode(clientId, "", null, userAuthHeader);
- testDeregisterPublicClient(clientId);
+ testDeregisterPublicClient(clientId, username);
response = requestTokenWithAuthorizationCodeAndForm(clientId,
clientSecret, code);
@@ -266,7 +260,7 @@
assertEquals(Status.METHOD_NOT_ALLOWED.getStatusCode(), response.getStatus());
}
- private void testDeregisterPublicClient (String clientId)
+ private void testDeregisterPublicClient (String clientId, String username)
throws UniformInterfaceException, ClientHandlerException,
KustvaktException {
@@ -467,7 +461,8 @@
node.at("/errors/0/1").asText());
}
- private void requestUserClientList (String userAuthHeader) throws KustvaktException {
+ private void requestAuthorizedClientList (String userAuthHeader)
+ throws KustvaktException {
MultivaluedMap<String, String> form = new MultivaluedMapImpl();
form.add("client_id", superClientId);
form.add("client_secret", clientSecret);
@@ -517,9 +512,10 @@
confidentialClientId, clientSecret, code);
assertEquals(Status.OK.getStatusCode(), response.getStatus());
- requestUserClientList(userAuthHeader);
- testListClientWithMultipleRefreshTokens(userAuthHeader);
-
+ requestAuthorizedClientList(userAuthHeader);
+ testListAuthorizedClientWithMultipleRefreshTokens(userAuthHeader);
+
+
testRequestTokenWithRevokedRefreshToken(publicClientId, clientSecret,
refreshToken);
@@ -531,8 +527,8 @@
accessToken, refreshToken);
}
- private void testListClientWithMultipleRefreshTokens (String userAuthHeader)
- throws KustvaktException {
+ private void testListAuthorizedClientWithMultipleRefreshTokens (
+ String userAuthHeader) throws KustvaktException {
// client 1
String code = requestAuthorizationCode(publicClientId, clientSecret,
null, userAuthHeader);
@@ -541,7 +537,7 @@
assertEquals(Status.OK.getStatusCode(), response.getStatus());
- requestUserClientList(userAuthHeader);
+ requestAuthorizedClientList(userAuthHeader);
JsonNode node = JsonUtils.readTree(response.getEntity(String.class));
String accessToken = node.at("/access_token").asText();
@@ -582,4 +578,38 @@
testRequestTokenWithRevokedRefreshToken(clientId, clientSecret,
refreshToken);
}
+
+ @Test
+ public void testListRegisteredClients ()
+ throws KustvaktException {
+
+ String clientName = "OAuth2DoryClient";
+ OAuth2ClientJson json = new OAuth2ClientJson();
+ json.setName(clientName);
+ json.setType(OAuth2ClientType.PUBLIC);
+ json.setDescription("This is dory client.");
+
+ registerClient("dory", json);
+
+ MultivaluedMap<String, String> form = new MultivaluedMapImpl();
+ form.add("client_id", superClientId);
+ form.add("client_secret", clientSecret);
+
+ ClientResponse response = resource().path(API_VERSION).path("oauth2")
+ .path("client").path("registered")
+ .header(Attributes.AUTHORIZATION, userAuthHeader)
+ .header(HttpHeaders.CONTENT_TYPE,
+ ContentType.APPLICATION_FORM_URLENCODED)
+ .entity(form).post(ClientResponse.class);
+
+ assertEquals(Status.OK.getStatusCode(), response.getStatus());
+
+ String entity = response.getEntity(String.class);
+ JsonNode node = JsonUtils.readTree(entity);
+
+ assertEquals(1, node.size());
+ assertEquals(clientName, node.at("/0/clientName").asText());
+ String clientId = node.at("/0/clientId").asText();
+ testDeregisterPublicClient(clientId, "dory");
+ }
}