Added a service to list active refresh tokens of a user.
Change-Id: I4f9508d9e12162bdf18fb2d61d3347000f29af0a
Change-Id: I4f9508d9e12162bdf18fb2d61d3347000f29af0a
diff --git a/full/Changes b/full/Changes
index e9e62c9..ae73b6f 100644
--- a/full/Changes
+++ b/full/Changes
@@ -22,6 +22,8 @@
15/11/2019
- Merged list authorized client and list registered client services
(margaretha)
+21/11/2019
+ - Added a service to list active refresh tokens of a user (margaretha)
# version 0.62.1
08/07/2019
diff --git a/full/src/main/java/de/ids_mannheim/korap/authentication/KustvaktAuthenticationManager.java b/full/src/main/java/de/ids_mannheim/korap/authentication/KustvaktAuthenticationManager.java
index 650a118..3fd5722 100644
--- a/full/src/main/java/de/ids_mannheim/korap/authentication/KustvaktAuthenticationManager.java
+++ b/full/src/main/java/de/ids_mannheim/korap/authentication/KustvaktAuthenticationManager.java
@@ -100,7 +100,7 @@
*/
@Override
public TokenContext getTokenContext(TokenType type, String token,
- String host, String useragent) throws KustvaktException {
+ String host, String userAgent) throws KustvaktException {
AuthenticationIface provider = getProvider(type , null);
@@ -111,6 +111,8 @@
}
TokenContext context = provider.getTokenContext(token);
+ context.setHostAddress(host);
+ context.setUserAgent(userAgent);
// if (!matchStatus(host, useragent, context))
// provider.removeUserSession(token);
return context;
diff --git a/full/src/main/java/de/ids_mannheim/korap/oauth2/dao/RefreshTokenDao.java b/full/src/main/java/de/ids_mannheim/korap/oauth2/dao/RefreshTokenDao.java
index 87c7a6d..339f3cd 100644
--- a/full/src/main/java/de/ids_mannheim/korap/oauth2/dao/RefreshTokenDao.java
+++ b/full/src/main/java/de/ids_mannheim/korap/oauth2/dao/RefreshTokenDao.java
@@ -12,6 +12,7 @@
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Join;
+import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import org.springframework.beans.factory.annotation.Autowired;
@@ -108,27 +109,26 @@
return token;
}
- // public List<RefreshToken> retrieveRefreshTokenByUser (String
- // username)
- // throws KustvaktException {
- // ParameterChecker.checkStringValue(username, "username");
- //
- // CriteriaBuilder builder = entityManager.getCriteriaBuilder();
- // CriteriaQuery<RefreshToken> query =
- // builder.createQuery(RefreshToken.class);
- //
- // Root<RefreshToken> root = query.from(RefreshToken.class);
- // Predicate condition = builder.and(
- // builder.equal(root.get(RefreshToken_.userId), username),
- // builder.equal(root.get(RefreshToken_.isRevoked), false),
- // builder.greaterThan(
- // root.<ZonedDateTime> get(RefreshToken_.expiryDate),
- // ZonedDateTime
- // .now(ZoneId.of(Attributes.DEFAULT_TIME_ZONE)))
- // );
- // query.select(root);
- // query.where(condition);
- // TypedQuery<RefreshToken> q = entityManager.createQuery(query);
- // return q.getResultList();
- // }
+ public List<RefreshToken> retrieveRefreshTokenByUser (String username)
+ throws KustvaktException {
+ ParameterChecker.checkStringValue(username, "username");
+
+ CriteriaBuilder builder = entityManager.getCriteriaBuilder();
+ CriteriaQuery<RefreshToken> query =
+ builder.createQuery(RefreshToken.class);
+
+ Root<RefreshToken> root = query.from(RefreshToken.class);
+ root.fetch(RefreshToken_.client);
+ Predicate condition = builder.and(
+ builder.equal(root.get(RefreshToken_.userId), username),
+ builder.equal(root.get(RefreshToken_.isRevoked), false),
+ builder.greaterThan(
+ root.<ZonedDateTime> get(RefreshToken_.expiryDate),
+ ZonedDateTime
+ .now(ZoneId.of(Attributes.DEFAULT_TIME_ZONE))));
+ query.select(root);
+ query.where(condition);
+ TypedQuery<RefreshToken> q = entityManager.createQuery(query);
+ return q.getResultList();
+ }
}
diff --git a/full/src/main/java/de/ids_mannheim/korap/oauth2/dto/OAuth2RefreshTokenDto.java b/full/src/main/java/de/ids_mannheim/korap/oauth2/dto/OAuth2RefreshTokenDto.java
new file mode 100644
index 0000000..8fac3ca
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/oauth2/dto/OAuth2RefreshTokenDto.java
@@ -0,0 +1,97 @@
+package de.ids_mannheim.korap.oauth2.dto;
+
+import java.util.Set;
+
+/**
+ * Describes OAuth2 refresh tokens
+ *
+ * @author margaretha
+ *
+ */
+public class OAuth2RefreshTokenDto {
+
+ private String token;
+ private String createdDate;
+ private String expiryDate;
+ private String userAuthenticationTime;
+ private Set<String> scopes;
+
+ private String clientId;
+ private String clientName;
+ private String clientDescription;
+ private String clientUrl;
+
+ public String getToken () {
+ return token;
+ }
+
+ public void setToken (String token) {
+ this.token = token;
+ }
+
+ public String getClientId () {
+ return clientId;
+ }
+
+ public void setClientId (String clientId) {
+ this.clientId = clientId;
+ }
+
+ public String getClientName () {
+ return clientName;
+ }
+
+ public void setClientName (String clientName) {
+ this.clientName = clientName;
+ }
+
+ public String getClientDescription () {
+ return clientDescription;
+ }
+
+ public void setClientDescription (String clientDescription) {
+ this.clientDescription = clientDescription;
+ }
+
+ public String getClientUrl () {
+ return clientUrl;
+ }
+
+ public void setClientUrl (String clientUrl) {
+ this.clientUrl = clientUrl;
+ }
+
+ public String getCreatedDate () {
+ return createdDate;
+ }
+
+ public void setCreatedDate (String createdDate) {
+ this.createdDate = createdDate;
+ }
+
+ public String getExpiryDate () {
+ return expiryDate;
+ }
+
+ public void setExpiryDate (String expiryDate) {
+ this.expiryDate = expiryDate;
+ }
+
+ public String getUserAuthenticationTime () {
+ return userAuthenticationTime;
+ }
+
+ public void setUserAuthenticationTime (
+ String userAuthenticationTime) {
+ this.userAuthenticationTime = userAuthenticationTime;
+ }
+
+ public Set<String> getScopes () {
+ return scopes;
+ }
+
+ public void setScopes (Set<String> scopes) {
+ this.scopes = scopes;
+ }
+
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/oauth2/oltu/service/OltuTokenService.java b/full/src/main/java/de/ids_mannheim/korap/oauth2/oltu/service/OltuTokenService.java
index c18ba88..feafc87 100644
--- a/full/src/main/java/de/ids_mannheim/korap/oauth2/oltu/service/OltuTokenService.java
+++ b/full/src/main/java/de/ids_mannheim/korap/oauth2/oltu/service/OltuTokenService.java
@@ -2,6 +2,8 @@
import java.time.ZoneId;
import java.time.ZonedDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@@ -25,6 +27,7 @@
import de.ids_mannheim.korap.oauth2.constant.OAuth2Error;
import de.ids_mannheim.korap.oauth2.dao.AccessTokenDao;
import de.ids_mannheim.korap.oauth2.dao.RefreshTokenDao;
+import de.ids_mannheim.korap.oauth2.dto.OAuth2RefreshTokenDto;
import de.ids_mannheim.korap.oauth2.entity.AccessScope;
import de.ids_mannheim.korap.oauth2.entity.AccessToken;
import de.ids_mannheim.korap.oauth2.entity.Authorization;
@@ -32,6 +35,7 @@
import de.ids_mannheim.korap.oauth2.entity.RefreshToken;
import de.ids_mannheim.korap.oauth2.oltu.OAuth2RevokeTokenRequest;
import de.ids_mannheim.korap.oauth2.oltu.OAuth2RevokeTokenSuperRequest;
+import de.ids_mannheim.korap.oauth2.service.OAuth2ClientService;
import de.ids_mannheim.korap.oauth2.service.OAuth2TokenService;
/** Implementation of token service using Apache Oltu.
@@ -49,6 +53,9 @@
private AccessTokenDao tokenDao;
@Autowired
private RefreshTokenDao refreshDao;
+
+ @Autowired
+ private OAuth2ClientService clientService;
public OAuthResponse requestAccessToken (
AbstractOAuthTokenRequest oAuthRequest)
@@ -438,4 +445,45 @@
revokeRefreshToken(r);
}
}
+
+ public List<OAuth2RefreshTokenDto> listUserRefreshToken (String username, String clientId,
+ String clientSecret) throws KustvaktException {
+
+ OAuth2Client client = clientService.authenticateClient(clientId, clientSecret);
+ if (!client.isSuper()) {
+ throw new KustvaktException(StatusCodes.CLIENT_AUTHORIZATION_FAILED,
+ "Only super client is allowed.",
+ OAuth2Error.UNAUTHORIZED_CLIENT);
+ }
+
+ List<RefreshToken> tokens = refreshDao.retrieveRefreshTokenByUser(username);
+ List<OAuth2RefreshTokenDto> dtoList = new ArrayList<>(tokens.size());
+ for (RefreshToken t : tokens){
+ OAuth2Client tokenClient = t.getClient();
+ if (tokenClient.getId().equals(client.getId())){
+ continue;
+ }
+ OAuth2RefreshTokenDto dto = new OAuth2RefreshTokenDto();
+ dto.setClientId(tokenClient.getId());
+ dto.setClientName(tokenClient.getName());
+ dto.setClientUrl(tokenClient.getUrl());
+ dto.setClientDescription(tokenClient.getDescription());
+
+ DateTimeFormatter f = DateTimeFormatter.ISO_DATE_TIME;
+ dto.setCreatedDate(t.getCreatedDate().format(f));
+ dto.setExpiryDate(t.getExpiryDate().format(f));
+ dto.setUserAuthenticationTime(
+ t.getUserAuthenticationTime().format(f));
+ dto.setToken(t.getToken());
+
+ Set<AccessScope> accessScopes = t.getScopes();
+ Set<String> scopes = new HashSet<>(accessScopes.size());
+ for (AccessScope s : accessScopes){
+ scopes.add(s.getId().toString());
+ }
+ dto.setScopes(scopes);
+ dtoList.add(dto);
+ }
+ return dtoList;
+ }
}
diff --git a/full/src/main/java/de/ids_mannheim/korap/web/controller/OAuth2Controller.java b/full/src/main/java/de/ids_mannheim/korap/web/controller/OAuth2Controller.java
index 0cd6171..bbe1122 100644
--- a/full/src/main/java/de/ids_mannheim/korap/web/controller/OAuth2Controller.java
+++ b/full/src/main/java/de/ids_mannheim/korap/web/controller/OAuth2Controller.java
@@ -1,6 +1,7 @@
package de.ids_mannheim.korap.web.controller;
import java.time.ZonedDateTime;
+import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.Consumes;
@@ -28,6 +29,7 @@
import de.ids_mannheim.korap.constant.OAuth2Scope;
import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.oauth2.dto.OAuth2RefreshTokenDto;
import de.ids_mannheim.korap.oauth2.oltu.OAuth2AuthorizationRequest;
import de.ids_mannheim.korap.oauth2.oltu.OAuth2RevokeTokenRequest;
import de.ids_mannheim.korap.oauth2.oltu.OAuth2RevokeTokenSuperRequest;
@@ -291,4 +293,28 @@
throw responseHandler.throwit(e);
}
}
+
+ @POST
+ @Path("token/list")
+ @ResourceFilters({ AuthenticationFilter.class, BlockingFilter.class })
+ @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
+ @Produces(MediaType.APPLICATION_JSON + ";charset=utf-8")
+ public List<OAuth2RefreshTokenDto> listUserRefreshToken (
+ @Context SecurityContext context,
+ @FormParam("client_id") String clientId,
+ @FormParam("client_secret") String clientSecret) {
+
+ TokenContext tokenContext = (TokenContext) context.getUserPrincipal();
+ String username = tokenContext.getUsername();
+
+ try {
+ return tokenService.listUserRefreshToken(username, clientId,
+ clientSecret);
+ }
+ catch (KustvaktException e) {
+ throw responseHandler.throwit(e);
+ }
+
+
+ }
}
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 e1cd67b..6a08801 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
@@ -25,9 +25,9 @@
import com.sun.jersey.core.util.MultivaluedMapImpl;
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.OAuth2Error;
-import de.ids_mannheim.korap.oauth2.dao.RefreshTokenDao;
import de.ids_mannheim.korap.oauth2.entity.AccessScope;
import de.ids_mannheim.korap.oauth2.entity.RefreshToken;
import de.ids_mannheim.korap.utils.JsonUtils;
@@ -39,7 +39,9 @@
public class OAuth2ControllerTest extends OAuth2TestBase {
public String userAuthHeader;
-
+ public static String ACCESS_TOKEN_TYPE = "access_token";
+ public static String REFRESH_TOKEN_TYPE = "refresh_token";
+
public OAuth2ControllerTest () throws KustvaktException {
userAuthHeader = HttpAuthorizationHandler
.createBasicAuthorizationHeaderValue("dory", "password");
@@ -181,7 +183,7 @@
assertNotNull(node.at("/expires_in").asText());
testRevokeToken(accessToken, publicClientId,null,
- "access_token");
+ ACCESS_TOKEN_TYPE);
testRequestRefreshTokenInvalidScope(publicClientId, refreshToken);
testRequestRefreshTokenInvalidClient(refreshToken);
@@ -225,8 +227,9 @@
String refreshToken = node.at("/refresh_token").asText();
testRevokeToken(refreshToken, confidentialClientId,clientSecret,
- "refresh_token");
- testRequestTokenWithRevokedRefreshToken(confidentialClientId, clientSecret, refreshToken);
+ REFRESH_TOKEN_TYPE);
+ testRequestTokenWithRevokedRefreshToken(confidentialClientId,
+ clientSecret, refreshToken);
}
private void testRequestTokenWithUsedAuthorization (String code)
@@ -663,4 +666,93 @@
assertEquals(Status.OK.getStatusCode(), response.getStatus());
}
+ @Test
+ public void testListRefreshToken () throws KustvaktException {
+ String username = "gurgle";
+ String password = "pwd";
+ userAuthHeader = HttpAuthorizationHandler
+ .createBasicAuthorizationHeaderValue(username, password);
+
+ // super client
+ ClientResponse response = requestTokenWithPassword(superClientId,
+ clientSecret, username, password);
+ assertEquals(Status.OK.getStatusCode(), response.getStatus());
+ JsonNode node = JsonUtils.readTree(response.getEntity(String.class));
+ String refreshToken1 = node.at("/refresh_token").asText();
+
+ // client 1
+ String code = requestAuthorizationCode(publicClientId, clientSecret,
+ null, userAuthHeader);
+ response = requestTokenWithAuthorizationCodeAndForm(publicClientId, "",
+ code);
+ assertEquals(Status.OK.getStatusCode(), response.getStatus());
+
+ // client 2
+ code = requestAuthorizationCode(confidentialClientId, clientSecret,
+ null, userAuthHeader);
+ response = requestTokenWithAuthorizationCodeAndForm(
+ confidentialClientId, clientSecret, code);
+ assertEquals(Status.OK.getStatusCode(), response.getStatus());
+
+ // list
+ node = requestRefreshTokenList(userAuthHeader);
+ assertEquals(2, node.size());
+ assertEquals(publicClientId, node.at("/0/clientId").asText());
+ assertEquals(confidentialClientId, node.at("/1/clientId").asText());
+
+ // client 1
+ code = requestAuthorizationCode(publicClientId, clientSecret,
+ null, userAuthHeader);
+ response = requestTokenWithAuthorizationCodeAndForm(
+ publicClientId, "", code);
+ assertEquals(Status.OK.getStatusCode(), response.getStatus());
+
+ // client 1
+ code = requestAuthorizationCode(publicClientId, clientSecret,
+ null, HttpAuthorizationHandler
+ .createBasicAuthorizationHeaderValue("darla", "pwd"));
+ assertEquals(Status.OK.getStatusCode(), response.getStatus());
+ response = requestTokenWithAuthorizationCodeAndForm(
+ publicClientId, "", code);
+
+ node = JsonUtils.readTree(response.getEntity(String.class));
+ String refreshToken5 = node.at("/refresh_token").asText();
+
+ node = requestRefreshTokenList(userAuthHeader);
+ assertEquals(3, node.size());
+
+ testRevokeToken(refreshToken1, superClientId, clientSecret,
+ REFRESH_TOKEN_TYPE);
+ testRevokeToken(node.at("/0/token").asText(), publicClientId, null,
+ REFRESH_TOKEN_TYPE);
+ testRevokeToken(node.at("/1/token").asText(), confidentialClientId,
+ clientSecret, REFRESH_TOKEN_TYPE);
+
+ node = requestRefreshTokenList(userAuthHeader);
+ assertEquals(1, node.size());
+
+ testRevokeToken(node.at("/0/token").asText(), publicClientId, null,
+ REFRESH_TOKEN_TYPE);
+ testRevokeToken(refreshToken5, publicClientId, null,
+ REFRESH_TOKEN_TYPE);
+ }
+
+ private JsonNode requestRefreshTokenList (String userAuthHeader)
+ throws KustvaktException {
+ 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("token").path("list")
+ .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);
+ return JsonUtils.readTree(entity);
+ }
}