Added revocation of access tokens and authorization code when
deregistering clients, and implemented reset client secret.
Change-Id: Ice92e3759678eac4d2322ff65a0997a003de357c
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 67503e1..5564ee7 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
@@ -74,6 +74,18 @@
entityManager.persist(accessToken);
}
+ public AccessToken updateAccessToken (AccessToken accessToken)
+ throws KustvaktException {
+ ParameterChecker.checkObjectValue(accessToken, "access_token");
+ AccessToken cachedToken =
+ (AccessToken) this.getCacheValue(accessToken.getToken());
+ if (cachedToken != null) {
+ this.removeCacheEntry(accessToken.getToken());
+ }
+
+ accessToken = entityManager.merge(accessToken);
+ return accessToken;
+ }
public AccessToken retrieveAccessToken (String accessToken)
throws KustvaktException {
@@ -121,19 +133,6 @@
return q.getResultList();
}
- public AccessToken updateAccessToken (AccessToken accessToken)
- throws KustvaktException {
- ParameterChecker.checkObjectValue(accessToken, "access_token");
- AccessToken cachedToken =
- (AccessToken) this.getCacheValue(accessToken.getId());
- if (cachedToken != null) {
- this.removeCacheEntry(cachedToken);
- }
-
- accessToken = entityManager.merge(accessToken);
- return accessToken;
- }
-
public AccessToken retrieveAccessTokenByAnynomousToken (String token)
throws KustvaktException {
ParameterChecker.checkObjectValue(token, "token");
@@ -165,4 +164,16 @@
"Access token is not found", OAuth2Error.INVALID_TOKEN);
}
}
+
+ @SuppressWarnings("unchecked")
+ public List<AccessToken> retrieveAccessTokenByClientId (String clientId) {
+ CriteriaBuilder builder = entityManager.getCriteriaBuilder();
+ CriteriaQuery<AccessToken> query =
+ builder.createQuery(AccessToken.class);
+ Root<AccessToken> root = query.from(AccessToken.class);
+ query.select(root);
+ query.where(builder.equal(root.get(AccessToken_.clientId), clientId));
+ Query q = entityManager.createQuery(query);
+ return q.getResultList();
+ }
}
diff --git a/full/src/main/java/de/ids_mannheim/korap/oauth2/dao/AuthorizationCacheDao.java b/full/src/main/java/de/ids_mannheim/korap/oauth2/dao/AuthorizationCacheDao.java
index eed72d4..af83e16 100644
--- a/full/src/main/java/de/ids_mannheim/korap/oauth2/dao/AuthorizationCacheDao.java
+++ b/full/src/main/java/de/ids_mannheim/korap/oauth2/dao/AuthorizationCacheDao.java
@@ -1,6 +1,9 @@
package de.ids_mannheim.korap.oauth2.dao;
import java.time.ZonedDateTime;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
import java.util.Set;
import de.ids_mannheim.korap.config.KustvaktCacheable;
@@ -11,6 +14,7 @@
import de.ids_mannheim.korap.oauth2.entity.Authorization;
import de.ids_mannheim.korap.oauth2.interfaces.AuthorizationDaoInterface;
import de.ids_mannheim.korap.utils.ParameterChecker;
+import net.sf.ehcache.Element;
public class AuthorizationCacheDao extends KustvaktCacheable
implements AuthorizationDaoInterface {
@@ -69,4 +73,19 @@
return auth;
}
+ @Override
+ public List<Authorization> retrieveAuthorizationsByClientId (
+ String clientId) {
+ List<Authorization> authList = new ArrayList<>();
+
+ Map<Object, Element> map = getAllCacheElements();
+ for (Object key : map.keySet()){
+ Authorization auth = (Authorization) map.get(key).getObjectValue();
+ if (auth.getClientId().equals(clientId)){
+ authList.add(auth);
+ }
+ }
+ return authList;
+ }
+
}
diff --git a/full/src/main/java/de/ids_mannheim/korap/oauth2/dao/AuthorizationDao.java b/full/src/main/java/de/ids_mannheim/korap/oauth2/dao/AuthorizationDao.java
index c62b130..ff82d9c 100644
--- a/full/src/main/java/de/ids_mannheim/korap/oauth2/dao/AuthorizationDao.java
+++ b/full/src/main/java/de/ids_mannheim/korap/oauth2/dao/AuthorizationDao.java
@@ -1,6 +1,7 @@
package de.ids_mannheim.korap.oauth2.dao;
import java.time.ZonedDateTime;
+import java.util.List;
import java.util.Set;
import javax.persistence.EntityManager;
@@ -25,14 +26,15 @@
@Transactional
@Repository
-public class AuthorizationDao implements AuthorizationDaoInterface{
+public class AuthorizationDao implements AuthorizationDaoInterface {
@PersistenceContext
private EntityManager entityManager;
public Authorization storeAuthorizationCode (String clientId, String userId,
String code, Set<AccessScope> scopes, String redirectURI,
- ZonedDateTime authenticationTime, String nonce) throws KustvaktException {
+ ZonedDateTime authenticationTime, String nonce)
+ throws KustvaktException {
ParameterChecker.checkStringValue(clientId, "client_id");
ParameterChecker.checkStringValue(userId, "userId");
ParameterChecker.checkStringValue(code, "authorization code");
@@ -85,4 +87,21 @@
authorization = entityManager.merge(authorization);
return authorization;
}
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public List<Authorization> retrieveAuthorizationsByClientId (String clientId) {
+ CriteriaBuilder builder = entityManager.getCriteriaBuilder();
+ CriteriaQuery<Authorization> query =
+ builder.createQuery(Authorization.class);
+ Root<Authorization> root = query.from(Authorization.class);
+
+ Predicate restrictions =
+ builder.equal(root.get(Authorization_.clientId), clientId);
+
+ query.select(root);
+ query.where(restrictions);
+ Query q = entityManager.createQuery(query);
+ return q.getResultList();
+ }
}
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 78c438c..2b8a2eb 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
@@ -84,11 +84,17 @@
}
}
- public void deregisterClient (OAuth2Client client) {
+ public void deregisterClient (OAuth2Client client) throws KustvaktException {
+ ParameterChecker.checkObjectValue(client, "client");
if (!entityManager.contains(client)) {
client = entityManager.merge(client);
}
entityManager.remove(client);
}
+ public void updateClient (OAuth2Client client) throws KustvaktException {
+ ParameterChecker.checkObjectValue(client, "client");
+ client = entityManager.merge(client);
+ }
+
}
diff --git a/full/src/main/java/de/ids_mannheim/korap/oauth2/interfaces/AuthorizationDaoInterface.java b/full/src/main/java/de/ids_mannheim/korap/oauth2/interfaces/AuthorizationDaoInterface.java
index f9c7280..8dcde58 100644
--- a/full/src/main/java/de/ids_mannheim/korap/oauth2/interfaces/AuthorizationDaoInterface.java
+++ b/full/src/main/java/de/ids_mannheim/korap/oauth2/interfaces/AuthorizationDaoInterface.java
@@ -1,6 +1,7 @@
package de.ids_mannheim.korap.oauth2.interfaces;
import java.time.ZonedDateTime;
+import java.util.List;
import java.util.Set;
import de.ids_mannheim.korap.exceptions.KustvaktException;
@@ -18,4 +19,6 @@
public Authorization updateAuthorization (Authorization authorization)
throws KustvaktException;
+
+ public List<Authorization> retrieveAuthorizationsByClientId (String clientId);
}
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 8b3a639..53027f4 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
@@ -5,6 +5,7 @@
import java.net.URISyntaxException;
import java.net.URL;
import java.sql.SQLException;
+import java.util.List;
import org.apache.commons.validator.routines.UrlValidator;
import org.springframework.beans.factory.annotation.Autowired;
@@ -19,8 +20,12 @@
import de.ids_mannheim.korap.interfaces.EncryptionIface;
import de.ids_mannheim.korap.oauth2.constant.OAuth2ClientType;
import de.ids_mannheim.korap.oauth2.constant.OAuth2Error;
+import de.ids_mannheim.korap.oauth2.dao.AccessTokenDao;
import de.ids_mannheim.korap.oauth2.dao.OAuth2ClientDao;
+import de.ids_mannheim.korap.oauth2.entity.AccessToken;
+import de.ids_mannheim.korap.oauth2.entity.Authorization;
import de.ids_mannheim.korap.oauth2.entity.OAuth2Client;
+import de.ids_mannheim.korap.oauth2.interfaces.AuthorizationDaoInterface;
import de.ids_mannheim.korap.web.input.OAuth2ClientJson;
/**
@@ -46,6 +51,10 @@
@Autowired
private OAuth2ClientDao clientDao;
@Autowired
+ private AccessTokenDao tokenDao;
+ @Autowired
+ private AuthorizationDaoInterface authorizationDao;
+ @Autowired
private AdminDao adminDao;
@Autowired
private UrlValidator redirectURIValidator;
@@ -172,7 +181,52 @@
if (adminDao.isAdmin(username)
|| client.getRegisteredBy().equals(username)) {
+
clientDao.deregisterClient(client);
+
+ // revoke all related authorization tokens
+ List<Authorization> authList = authorizationDao
+ .retrieveAuthorizationsByClientId(clientId);
+ for (Authorization authorization : authList){
+ authorization.setRevoked(true);
+ authorizationDao.updateAuthorization(authorization);
+ }
+
+ // revoke all related access tokens
+ List<AccessToken> tokens =
+ tokenDao.retrieveAccessTokenByClientId(clientId);
+ for (AccessToken token : tokens) {
+ token.setRevoked(true);
+ token.setRefreshTokenRevoked(true);
+ tokenDao.updateAccessToken(token);
+ }
+ }
+ else {
+ throw new KustvaktException(StatusCodes.AUTHORIZATION_FAILED,
+ "Unauthorized operation for user: " + username, username);
+ }
+ }
+
+ public OAuth2ClientDto resetSecret (String clientId, String clientSecret,
+ String username) throws KustvaktException {
+
+ OAuth2Client client = authenticateClient(clientId, clientSecret);
+ if (!client.getType().equals(OAuth2ClientType.CONFIDENTIAL)) {
+ throw new KustvaktException(
+ StatusCodes.NOT_ALLOWED,
+ "Operation is not allowed for public clients",
+ OAuth2Error.INVALID_REQUEST);
+ }
+ if (adminDao.isAdmin(username)
+ || client.getRegisteredBy().equals(username)) {
+
+ String secret = codeGenerator.createRandomCode();
+ String secretHashcode = encryption.secureHash(secret,
+ config.getPasscodeSaltField());
+
+ client.setSecret(secretHashcode);
+ clientDao.updateClient(client);
+ return new OAuth2ClientDto(clientId, secret);
}
else {
throw new KustvaktException(StatusCodes.AUTHORIZATION_FAILED,
@@ -197,15 +251,7 @@
public void authenticateClient (OAuth2Client client, String clientSecret)
throws KustvaktException {
- if (clientSecret == null) {
- if (client.getType().equals(OAuth2ClientType.CONFIDENTIAL)) {
- throw new KustvaktException(
- StatusCodes.CLIENT_AUTHENTICATION_FAILED,
- "Missing parameters: client_secret",
- OAuth2Error.INVALID_REQUEST);
- }
- }
- else if (clientSecret.isEmpty()) {
+ if (clientSecret == null || clientSecret.isEmpty()) {
if (client.getType().equals(OAuth2ClientType.CONFIDENTIAL)) {
throw new KustvaktException(
StatusCodes.CLIENT_AUTHENTICATION_FAILED,
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 e78aa2a..61f8237 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
@@ -24,7 +24,6 @@
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
-import com.sun.jersey.api.client.ClientResponse.Status;
import com.sun.jersey.spi.container.ResourceFilters;
import de.ids_mannheim.korap.exceptions.KustvaktException;
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 61295e7..293685e 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
@@ -29,8 +29,8 @@
/**
* Defines controllers for OAuth2 clients, namely applications
- * attempting
- * to access users' resources.
+ * performing actions such as searching and retrieving match
+ * information on behalf of users.
*
* @author margaretha
*
@@ -117,4 +117,35 @@
throw responseHandler.throwit(e);
}
}
+
+ /**
+ * Resets client secret of the given client. This controller
+ * requires client owner and client authentication. Only
+ * confidential clients are issued client secrets.
+ *
+ * @param securityContext
+ * @param clientId
+ * @param clientSecret
+ * @return a new client secret
+ */
+ @POST
+ @Path("reset")
+ @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
+ @Produces(MediaType.APPLICATION_JSON + ";charset=utf-8")
+ @ResourceFilters({ AuthenticationFilter.class, BlockingFilter.class })
+ public OAuth2ClientDto resetClientSecret (
+ @Context SecurityContext securityContext,
+ @FormParam("client_id") String clientId,
+ @FormParam("client_secret") String clientSecret) {
+ TokenContext context =
+ (TokenContext) securityContext.getUserPrincipal();
+ try {
+ return clientService.resetSecret(clientId, clientSecret,
+ context.getUsername());
+ }
+ catch (KustvaktException e) {
+ throw responseHandler.throwit(e);
+ }
+ }
+
}
diff --git a/full/src/main/java/de/ids_mannheim/korap/web/filter/AuthenticationFilter.java b/full/src/main/java/de/ids_mannheim/korap/web/filter/AuthenticationFilter.java
index 88603cc..d0a6cf6 100644
--- a/full/src/main/java/de/ids_mannheim/korap/web/filter/AuthenticationFilter.java
+++ b/full/src/main/java/de/ids_mannheim/korap/web/filter/AuthenticationFilter.java
@@ -75,7 +75,8 @@
context = authenticationManager.getTokenContext(
TokenType.BEARER, authData.getToken(), host,
ua);
- if (request.getPath().startsWith("vc/access")
+ if (request.getPath().startsWith("oauth2")
+ || request.getPath().startsWith("vc/access")
|| request.getPath().startsWith("vc/delete")
|| request.getPath().startsWith("group")
|| request.getPath().startsWith("user")) {