Compare commits

...

8 Commits

Author SHA1 Message Date
Mauro Mugnaini eb383671aa Extracted the UMA issuing code for logged user from the Valve in the `threadlocal-vars-cleaner` project to be used also after the login process for UMA issue in the context, since the Valve has already finished its work at that moment. (#20591) 2021-03-17 19:09:28 +01:00
Massimiliano Assante 6e67b8dae2 lowered the level of log "OIDC token is null in cache proxy" 2021-02-10 15:56:46 +01:00
Massimiliano Assante 62a6375372 Merge pull request 'The UMA refresh token flow is skipped by the specific setting (constant in the class) and a new token is requested if the old is expired.' (#7) from mauro.mugnaini/threadlocal-vars-cleaner:master into master 2021-01-22 15:01:57 +01:00
Massimiliano Assante f67c570402 Merge pull request 'Porting to master repo of the changes that seems to resolves issues related to ticket #20445' (#6) from mauro.mugnaini/threadlocal-vars-cleaner:master into master 2021-01-20 12:35:51 +01:00
Massimiliano Assante 6c7961c7b0 Merge pull request 'Moved out synch from method to session object and added checks for response messages from Keycloak. Added also redirection to logout URI in case of revoked or invalid OIDC token.' (#5) from mauro.mugnaini/threadlocal-vars-cleaner:master into master 2021-01-11 16:45:41 +01:00
Massimiliano Assante 52433442c2 Merge pull request 'Rewrote the flow according to last tests and strange LR behavior in session management' (#4) from mauro.mugnaini/threadlocal-vars-cleaner:master into master 2020-12-22 11:13:02 +01:00
Massimiliano Assante fbe94b6fd1 updated date of last commit in changelog 2020-12-17 10:16:13 +01:00
Massimiliano Assante 91465f9a8e Merge pull request 'Forced refresh of access token when current scope changes' (#3) from mauro.mugnaini/threadlocal-vars-cleaner:master into master 2020-12-16 16:03:02 +01:00
3 changed files with 10 additions and 238 deletions

View File

@ -4,9 +4,7 @@
All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [v2.3.0-SNAPSHOT]
## [v2.2.1] - 2020-07-21
## [v2.2.1] - 2020-12-17
Added support for OIDC related communications

View File

@ -11,7 +11,7 @@
<groupId>org.gcube.portal</groupId>
<artifactId>threadlocal-vars-cleaner</artifactId>
<version>2.3.0-SNAPSHOT</version>
<version>2.2.1</version>
<packaging>jar</packaging>
<name>threadlocal-vars-cleaner</name>
<description>This component clean the Smartgears ThreadLocal variables each time a new Thread is assigned to a request from tomcat thread pool</description>

View File

@ -3,8 +3,6 @@ package org.gcube.portal.threadlocalexec;
import static org.gcube.common.authorization.client.Constants.authorizationService;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;
@ -23,12 +21,7 @@ import org.gcube.common.authorization.library.provider.UmaJWTProvider;
import org.gcube.common.authorization.library.provider.UserInfo;
import org.gcube.common.portal.PortalContext;
import org.gcube.common.scope.api.ScopeProvider;
import org.gcube.oidc.rest.JWTToken;
import org.gcube.oidc.rest.OpenIdConnectConfiguration;
import org.gcube.oidc.rest.OpenIdConnectRESTHelper;
import org.gcube.oidc.rest.OpenIdConnectRESTHelperException;
import org.gcube.portal.oidc.lr62.JWTCacheProxy;
import org.gcube.portal.oidc.lr62.LiferayOpenIdConnectConfiguration;
import org.gcube.portal.oidc.lr62.OIDCUmaUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -48,14 +41,6 @@ public class SmartGearsPortalValve extends ValveBase {
private final static String DEFAULT_ROLE = "OrganizationMember";
private final static String LIFERAY_POLLER_CONTEXT = "poller/receive";
private static boolean REFRESH_UMA_TOKEN = false;
private static String LOGOUT_URI = "/c/portal/logout";
private static boolean FORCE_LOGOUT_ON_INVALID_OIDC = true;
private static boolean FORCE_LOGOUT_ON_MISSING_OIDC = true;
private static boolean FORCE_LOGOUT_ON_OIDC_REFRESH_ERROR = true;
private static final int MAX_AUTHORIZATION_RETRY_ATTEMPTS = 4;
private static final int AUTHORIZATION_RETRY_ATTEMPTS_DELAY = 4000;
@Override
public void invoke(Request req, Response resp) throws IOException, ServletException {
SecurityTokenProvider.instance.reset();
@ -83,7 +68,13 @@ public class SmartGearsPortalValve extends ValveBase {
_log.error("Something went wrong in generating token for " + username + " in scope " + scope);
e.printStackTrace();
}
checkUMATicket(request, (HttpServletResponse) resp, scope);
_log.debug("Getting current user");
User user = getCurrentUser(request);
_log.debug("Getting current session");
HttpSession session = request.getSession(false);
OIDCUmaUtil.checkUMATicketAndProvideInThreadLocal(request, (HttpServletResponse) resp, user,
session, scope);
//_log.trace("Security token set OK for " + username + " in scope " + scope);
}
@ -92,223 +83,6 @@ public class SmartGearsPortalValve extends ValveBase {
getNext().invoke(req, resp);
}
private void checkUMATicket(HttpServletRequest request, HttpServletResponse response, String scope) {
_log.debug("Getting current user");
User user = getCurrentUser(request);
if (user == null) {
_log.error("Current user not found, cannot continue");
return;
}
_log.debug("Current user is: {} [{}]", user.getScreenName(), user.getEmailAddress());
HttpSession session = request.getSession(false);
if (session == null) {
_log.debug("Session is null, cannot continue");
return;
}
String sessionId = session.getId();
_log.debug("Current session ID is {}", sessionId);
String urlEncodedScope = null;
try {
urlEncodedScope = URLEncoder.encode(scope, "UTF-8");
} catch (UnsupportedEncodingException e) {
// Almost impossible
_log.error("Cannot URL encode scope", e);
return;
}
_log.debug("URL encoded scope is: {}", urlEncodedScope);
JWTToken umaToken = null;
synchronized (JWTCacheProxy.getInstance().getMutexFor(user)) {
_log.trace("Getting UMA token for user {}, and session {}", user.getScreenName(), sessionId);
umaToken = JWTCacheProxy.getInstance().getUMAToken(user, sessionId);
if (umaToken != null && !umaToken.isExpired() && umaToken.getAud().contains(urlEncodedScope)) {
_log.trace("Current UMA token is OK {}", umaToken.getTokenEssentials());
} else {
if (umaToken != null && umaToken.getAud().contains(urlEncodedScope) && umaToken.isExpired()) {
if (REFRESH_UMA_TOKEN) {
_log.debug("Suitable UMA token found but is expired, trying to refresh it {}",
umaToken.getTokenEssentials());
OpenIdConnectConfiguration configuration = LiferayOpenIdConnectConfiguration
.getConfiguration(request);
try {
umaToken = OpenIdConnectRESTHelper.refreshToken(configuration.getTokenURL(), umaToken);
_log.debug("Got a refreshed UMA token {}", umaToken.getTokenEssentials());
_log.debug("Setting the refreshed UMA token in cache proxy for user {}, and session]",
user.getScreenName(), sessionId);
JWTCacheProxy.getInstance().setUMAToken(user, sessionId, umaToken);
} catch (OpenIdConnectRESTHelperException e) {
if (e.hasJSONPayload()) {
if (OpenIdConnectRESTHelper.isInvalidBearerTokenError(e.getResponseString())) {
if (FORCE_LOGOUT_ON_INVALID_OIDC) {
_log.warn("OIDC token is become invalid, forcing redirect to logout URI");
forceLogout(response);
} else {
_log.warn("OIDC token is become invalid, cannot continue");
}
return;
} else if (OpenIdConnectRESTHelper.isTokenNotActiveError(e.getResponseString())) {
_log.info("UMA token is no more active, get new one");
} else {
_log.error("Other UMA token refresh error", e);
}
} else {
_log.error("Refreshing UMA token on server " + umaToken.getTokenEssentials(), e);
}
umaToken = null;
_log.debug(
"Removing inactive UMA token from cache proxy if present for user {} and session {}",
user.getScreenName(), sessionId);
JWTCacheProxy.getInstance().removeUMAToken(user, sessionId);
}
} else {
_log.debug("Suitable UMA token found but it is expired."
+ "It will be replaced with new one according to settings {}",
umaToken.getTokenEssentials());
umaToken = null;
_log.debug("Removing inactive UMA token from cache proxy if present for user {} and session {}",
user.getScreenName(), sessionId);
JWTCacheProxy.getInstance().removeUMAToken(user, sessionId);
}
}
if (umaToken == null || !umaToken.getAud().contains(urlEncodedScope)) {
boolean scopeIsChanged = false;
if (umaToken == null) {
_log.debug("Getting new UMA token for scope {}", urlEncodedScope);
} else if (!umaToken.getAud().contains(urlEncodedScope)) {
scopeIsChanged = true;
_log.info("Getting new UMA token for scope {} since it has been issued for another scope {}",
urlEncodedScope, umaToken.getTokenEssentials());
}
_log.debug("Getting OIDC token from cache proxy for user {} and session {}", user.getScreenName(),
sessionId);
JWTToken authToken = JWTCacheProxy.getInstance().getOIDCToken(user, sessionId);
if (authToken == null) {
if (FORCE_LOGOUT_ON_MISSING_OIDC) {
_log.warn("OIDC token is null in cache proxy, force redirecting to logut URI");
forceLogout(response);
} else {
_log.error("OIDC token is null in cache proxy, cannot continue!");
}
return;
} else {
_log.debug("OIDC token is {}", authToken.getTokenEssentials());
}
OpenIdConnectConfiguration configuration = LiferayOpenIdConnectConfiguration
.getConfiguration(request);
boolean isNotAuthorized = false;
int authorizationAttempts = 0;
do {
try {
if (isNotAuthorized || scopeIsChanged || authToken.isExpired()) {
if (isNotAuthorized) {
_log.info(
"UMA token is not authorized with current OIDC token, "
+ "refreshing it to be sure that new grants are present. "
+ "[attempts: {}]",
authorizationAttempts);
// Resetting the flag to be sure to have correct log message each loop
isNotAuthorized = false;
} else if (scopeIsChanged) {
_log.info(
"Scope is changed, refreshing token to be sure that new grants are present");
} else if (authToken.isExpired()) {
_log.debug("OIDC token is expired, trying to refresh it {}",
authToken.getTokenEssentials());
}
try {
authToken = OpenIdConnectRESTHelper.refreshToken(configuration.getTokenURL(),
authToken);
} catch (OpenIdConnectRESTHelperException e) {
if (FORCE_LOGOUT_ON_OIDC_REFRESH_ERROR) {
_log.warn("Error refreshing OIDC token, force redirecting to logut URI");
forceLogout(response);
} else {
_log.error("Refreshing OIDC token on server", e);
}
return;
}
_log.debug("Setting refreshed OIDC token in cache proxy");
JWTCacheProxy.getInstance().setOIDCToken(user, sessionId, authToken);
}
_log.info("Getting UMA token from OIDC endpoint for scope: " + urlEncodedScope);
umaToken = OpenIdConnectRESTHelper.queryUMAToken(configuration.getTokenURL(),
authToken.getAccessTokenAsBearer(), urlEncodedScope, null);
_log.debug("Got new UMA token {}", umaToken.getTokenEssentials());
} catch (OpenIdConnectRESTHelperException e) {
if (e.hasJSONPayload()) {
if (OpenIdConnectRESTHelper.isInvalidBearerTokenError(e.getResponseString())) {
if (FORCE_LOGOUT_ON_INVALID_OIDC) {
_log.warn("OIDC token is become invalid, forcing redirect to logout URI");
forceLogout(response);
} else {
_log.error("OIDC token is become invalid, cannot continue");
}
return;
} else if (OpenIdConnectRESTHelper
.isAccessDeniedNotAuthorizedError(e.getResponseString())) {
_log.info("UMA token is" + (isNotAuthorized ? " still" : "")
+ " not authorized with actual OIDC token");
isNotAuthorized = true;
authorizationAttempts += 1;
if (authorizationAttempts <= MAX_AUTHORIZATION_RETRY_ATTEMPTS) {
_log.debug("Sleeping " + AUTHORIZATION_RETRY_ATTEMPTS_DELAY
+ " ms and looping refreshing the OIDC");
try {
Thread.sleep(AUTHORIZATION_RETRY_ATTEMPTS_DELAY);
} catch (InterruptedException ie) {
ie.printStackTrace();
}
} else {
_log.warn("OIDC token refresh attempts exhausted");
return;
}
}
} else {
_log.error("Getting UMA token from server", e);
return;
}
}
} while (isNotAuthorized);
}
_log.debug("Setting UMA token in cache proxy for user {} and session {}", user.getScreenName(),
sessionId);
JWTCacheProxy.getInstance().setUMAToken(user, sessionId, umaToken);
}
}
_log.trace("Current UMA token in use is: {}", umaToken.getTokenEssentials());
_log.debug("Setting UMA token with jti {} in UMA JWT provider", umaToken.getJti());
UmaJWTProvider.instance.set(umaToken.getRaw());
}
protected void forceLogout(HttpServletResponse response) {
try {
if (!response.isCommitted()) {
response.sendRedirect(LOGOUT_URI);
} else {
_log.warn("Cannot redirect to logout URI since the response is already commited");
}
} catch (IOException e) {
_log.error("Cannot redirect to logout URI: " + LOGOUT_URI, e);
}
}
/**
*
* @param context