Updated OAuth2 token list with token type and user clientId parameters.
Change-Id: Id7171fa09913cb2ab8bcefd7bce3003388ce93d4
diff --git a/full/src/main/java/de/ids_mannheim/korap/oauth2/dao/AccessTokenDao.java b/full/src/main/java/de/ids_mannheim/korap/oauth2/dao/AccessTokenDao.java
index b82819d..f3cc60a 100644
--- a/full/src/main/java/de/ids_mannheim/korap/oauth2/dao/AccessTokenDao.java
+++ b/full/src/main/java/de/ids_mannheim/korap/oauth2/dao/AccessTokenDao.java
@@ -183,4 +183,32 @@
return q.getResultList();
}
+ public List<AccessToken> retrieveAccessTokenByUser (String username, String clientId)
+ throws KustvaktException {
+ ParameterChecker.checkStringValue(username, "username");
+
+ CriteriaBuilder builder = entityManager.getCriteriaBuilder();
+ CriteriaQuery<AccessToken> query =
+ builder.createQuery(AccessToken.class);
+
+ Root<AccessToken> root = query.from(AccessToken.class);
+ root.fetch(AccessToken_.client);
+ Predicate condition = builder.and(
+ builder.equal(root.get(AccessToken_.userId), username),
+ builder.equal(root.get(AccessToken_.isRevoked), false),
+ builder.greaterThan(
+ root.<ZonedDateTime> get(AccessToken_.expiryDate),
+ ZonedDateTime
+ .now(ZoneId.of(Attributes.DEFAULT_TIME_ZONE))));
+ if (clientId != null && !clientId.isEmpty()) {
+ OAuth2Client client = clientDao.retrieveClientById(clientId);
+ condition = builder.and(condition,
+ builder.equal(root.get(AccessToken_.client), client));
+ }
+
+ query.select(root);
+ query.where(condition);
+ TypedQuery<AccessToken> q = entityManager.createQuery(query);
+ return q.getResultList();
+ }
}
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 0bcc54d..585d8b6 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
@@ -143,7 +143,8 @@
return q.getResultList();
}
- public List<RefreshToken> retrieveRefreshTokenByUser (String username)
+ public List<RefreshToken> retrieveRefreshTokenByUser (String username,
+ String clientId)
throws KustvaktException {
ParameterChecker.checkStringValue(username, "username");
@@ -160,6 +161,12 @@
root.<ZonedDateTime> get(RefreshToken_.expiryDate),
ZonedDateTime
.now(ZoneId.of(Attributes.DEFAULT_TIME_ZONE))));
+ if (clientId != null && !clientId.isEmpty()) {
+ OAuth2Client client = clientDao.retrieveClientById(clientId);
+ condition = builder.and(condition,
+ builder.equal(root.get(RefreshToken_.client), client));
+ }
+
query.select(root);
query.where(condition);
TypedQuery<RefreshToken> q = entityManager.createQuery(query);
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 abab7ce..d066063 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
@@ -104,7 +104,7 @@
* credentials.
*
* TODO: should create a new refresh token when the old refresh
- * token is used
+ * token is used (DONE)
*
* @param refreshTokenStr
* @param scopes
@@ -322,9 +322,9 @@
* Base64.
*
* <br /><br />
- * Additionally, a refresh token is issued. It can be used to
- * request a new access token without requiring user
- * reauthentication.
+ * Additionally, a refresh token is issued for confidential clients.
+ * It can be used to request a new access token without requiring user
+ * re-authentication.
*
* @param scopes
* a set of access token scopes in String
@@ -512,17 +512,17 @@
}
}
- public List<OAuth2TokenDto> listUserRefreshToken (String username, String clientId,
- String clientSecret) throws KustvaktException {
+ public List<OAuth2TokenDto> listUserRefreshToken (String username, String superClientId,
+ String superClientSecret, String clientId) throws KustvaktException {
- OAuth2Client client = clientService.authenticateClient(clientId, clientSecret);
+ OAuth2Client client = clientService.authenticateClient(superClientId, superClientSecret);
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<RefreshToken> tokens = refreshDao.retrieveRefreshTokenByUser(username, clientId);
List<OAuth2TokenDto> dtoList = new ArrayList<>(tokens.size());
for (RefreshToken t : tokens){
OAuth2Client tokenClient = t.getClient();
@@ -552,6 +552,48 @@
}
return dtoList;
}
+
+ public List<OAuth2TokenDto> listUserAccessToken (String username, String superClientId,
+ String superClientSecret, String clientId) throws KustvaktException {
+
+ OAuth2Client superClient = clientService.authenticateClient(superClientId, superClientSecret);
+ if (!superClient.isSuper()) {
+ throw new KustvaktException(StatusCodes.CLIENT_AUTHORIZATION_FAILED,
+ "Only super client is allowed.",
+ OAuth2Error.UNAUTHORIZED_CLIENT);
+ }
+
+ List<AccessToken> tokens =
+ tokenDao.retrieveAccessTokenByUser(username, clientId);
+ List<OAuth2TokenDto> dtoList = new ArrayList<>(tokens.size());
+ for (AccessToken t : tokens){
+ OAuth2Client tokenClient = t.getClient();
+ if (tokenClient.getId().equals(superClient.getId())){
+ continue;
+ }
+ OAuth2TokenDto dto = new OAuth2TokenDto();
+ 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;
+ }
public String clearAccessTokenCache (String adminToken, String accessToken,
ServletContext context) throws KustvaktException {
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 17fb0a4..e043837 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
@@ -30,6 +30,8 @@
import de.ids_mannheim.korap.constant.OAuth2Scope;
import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.exceptions.StatusCodes;
+import de.ids_mannheim.korap.oauth2.constant.OAuth2Error;
import de.ids_mannheim.korap.oauth2.dto.OAuth2TokenDto;
import de.ids_mannheim.korap.oauth2.oltu.OAuth2AuthorizationRequest;
import de.ids_mannheim.korap.oauth2.oltu.OAuth2RevokeAllTokenSuperRequest;
@@ -130,13 +132,20 @@
* Grants a client an access token, namely a string used in
* authenticated requests representing user authorization for
* the client to access user resources. An additional refresh
- * token strictly associated to the access token is also granted.
+ * token strictly associated to the access token is also granted
+ * for confidential clients. Both public and confidential clients
+ * may issue multiple access tokens.
*
* <br /><br />
*
- * Clients may request refresh access token using this endpoint.
- * This request will grants a new access token. The refresh token
- * is not changed and can be used until it expires.
+ * Confidential clients may request refresh access token using
+ * this endpoint. This request will grant a new access token.
+ *
+ * Usually the given refresh token is not changed and can be used
+ * until it expires. However, currently there is a limitation of
+ * one access token per one refresh token. Thus, the given refresh
+ * token will be revoked, and a new access token and a new refresh
+ * token will be returned.
*
* <br /><br />
*
@@ -260,7 +269,6 @@
}
}
-
@POST
@Path("revoke/super")
@ResourceFilters({ AuthenticationFilter.class, BlockingFilter.class })
@@ -290,7 +298,7 @@
throw responseHandler.throwit(e);
}
}
-
+
/**
* Revokes all tokens of a client for the authenticated user from
* a super client. This service is not part of the OAUTH2
@@ -343,22 +351,35 @@
@Produces(MediaType.APPLICATION_JSON + ";charset=utf-8")
public List<OAuth2TokenDto> listUserRefreshToken (
@Context SecurityContext context,
- @FormParam("client_id") String clientId,
- @FormParam("client_secret") String clientSecret) {
+ @FormParam("super_client_id") String superClientId,
+ @FormParam("super_client_secret") String superClientSecret,
+ @FormParam("client_id") String clientId, // optional
+ @FormParam("token_type") String tokenType) {
TokenContext tokenContext = (TokenContext) context.getUserPrincipal();
String username = tokenContext.getUsername();
try {
- return tokenService.listUserRefreshToken(username, clientId,
- clientSecret);
+ if (tokenType.equals("access_token")) {
+ return tokenService.listUserAccessToken(username, superClientId,
+ superClientSecret, clientId);
+ }
+ else if (tokenType.equals("refresh_token")) {
+ return tokenService.listUserRefreshToken(username,
+ superClientId, superClientSecret, clientId);
+ }
+ else {
+ throw new KustvaktException(StatusCodes.MISSING_PARAMETER,
+ "Missing token_type parameter value",
+ OAuth2Error.INVALID_REQUEST);
+ }
}
catch (KustvaktException e) {
throw responseHandler.throwit(e);
}
}
-
+
@POST
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
@Path("token/clear")
@@ -367,8 +388,8 @@
@FormParam("access_token") String accessToken,
@Context ServletContext context) {
try {
- String response = tokenService.clearAccessTokenCache(adminToken, accessToken,
- context);
+ String response = tokenService.clearAccessTokenCache(adminToken,
+ accessToken, context);
return Response.ok(response).build();
}
catch (KustvaktException e) {