diff --git a/.classpath b/.classpath index c40c8e2..d0d8276 100644 --- a/.classpath +++ b/.classpath @@ -1,27 +1,28 @@ - + - - + - - + + + - + + + - diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs index ec4300d..2f5cc74 100644 --- a/.settings/org.eclipse.jdt.core.prefs +++ b/.settings/org.eclipse.jdt.core.prefs @@ -1,5 +1,8 @@ eclipse.preferences.version=1 -org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7 -org.eclipse.jdt.core.compiler.compliance=1.7 +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 +org.eclipse.jdt.core.compiler.compliance=1.8 +org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning -org.eclipse.jdt.core.compiler.source=1.7 +org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=ignore +org.eclipse.jdt.core.compiler.release=disabled +org.eclipse.jdt.core.compiler.source=1.8 diff --git a/.settings/org.eclipse.m2e.core.prefs b/.settings/org.eclipse.m2e.core.prefs index f897a7f..4528a36 100644 --- a/.settings/org.eclipse.m2e.core.prefs +++ b/.settings/org.eclipse.m2e.core.prefs @@ -1,4 +1,4 @@ -activeProfiles= +activeProfiles=gcube-developer eclipse.preferences.version=1 resolveWorkspaceProjects=true version=1 diff --git a/pom.xml b/pom.xml index 10a66b6..0c5ae6d 100644 --- a/pom.xml +++ b/pom.xml @@ -11,7 +11,7 @@ org.gcube.portal threadlocal-vars-cleaner - 2.1.1 + 2.2.0 jar threadlocal-vars-cleaner This component clean the Smartgears ThreadLocal variables each time a new Thread is assigned to a request from tomcat thread pool @@ -61,11 +61,22 @@ portal-manager provided + + org.gcube.portal + oidc-library-portal + [0.1.0,) + provided + com.liferay.portal portal-service provided + + javax.portlet + portlet-api + provided + org.slf4j slf4j-api @@ -81,21 +92,21 @@ threadlocal-vars-cleaner-${project.version} - - org.apache.maven.plugins - maven-war-plugin - - - compile - - exploded - - - - - ${webappDirectory} - - + + + + + + + + + + + + + + + org.apache.maven.plugins maven-compiler-plugin diff --git a/src/main/java/org/gcube/portal/threadlocalexec/SmartGearsPortalValve.java b/src/main/java/org/gcube/portal/threadlocalexec/SmartGearsPortalValve.java index c916f06..72a587b 100644 --- a/src/main/java/org/gcube/portal/threadlocalexec/SmartGearsPortalValve.java +++ b/src/main/java/org/gcube/portal/threadlocalexec/SmartGearsPortalValve.java @@ -3,11 +3,14 @@ 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; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpSession; import org.apache.catalina.connector.Request; import org.apache.catalina.connector.Response; @@ -15,117 +18,237 @@ import org.apache.catalina.valves.ValveBase; import org.gcube.common.authorization.client.exceptions.ObjectNotFound; import org.gcube.common.authorization.library.provider.AuthorizationProvider; import org.gcube.common.authorization.library.provider.SecurityTokenProvider; +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.common.scope.impl.ScopeBean; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.liferay.portal.model.User; import com.liferay.portal.service.UserLocalServiceUtil; +import com.nubisware.oidc.lr62.JWTTokenUtil; +import com.nubisware.oidc.lr62.LiferayOpenIdConnectConfiguration; +import com.nubisware.oidc.lr62.OIDCTokenProxy; +import com.nubisware.oidc.rest.JWTToken; +import com.nubisware.oidc.rest.OpenIdConnectConfiguration; +import com.nubisware.oidc.rest.OpenIdConnectRESTHelper; + /** * * @author Massimiliano Assante, CNR ISTI * @author Lucio Lelii, CNR ISTI + * @author Mauro Mugnaini, Nubisware S.r.l. * */ -public class SmartGearsPortalValve extends ValveBase { - private static final Logger _log = LoggerFactory.getLogger(SmartGearsPortalValve.class); - private final static String DEFAULT_ROLE = "OrganizationMember"; - private final static String LIFERAY_POLLER_CONTEXT = "poller/receive"; +public class SmartGearsPortalValve extends ValveBase { + private static final Logger _log = LoggerFactory.getLogger(SmartGearsPortalValve.class); + private final static String DEFAULT_ROLE = "OrganizationMember"; + private final static String LIFERAY_POLLER_CONTEXT = "poller/receive"; - @Override - public void invoke(Request req, Response resp) throws IOException, ServletException { - SecurityTokenProvider.instance.reset(); - ScopeProvider.instance.reset(); - AuthorizationProvider.instance.reset(); - //_log.trace("SmartGearsPortalValve SecurityTokenProvider and AuthorizationProvider reset OK"); - if (req instanceof HttpServletRequest) { - HttpServletRequest request = (HttpServletRequest) req; - if (!req.getRequestURL().toString().endsWith(LIFERAY_POLLER_CONTEXT)) { //avoid calling gCube auth service for liferay internal poller - PortalContext context = PortalContext.getConfiguration(); - String scope = context.getCurrentScope(request); - String username = getCurrentUsername(request); - if (scope != null && username != null && validateContext(scope)) { - String userToken = null; - try { - ScopeProvider.instance.set(scope); - userToken = authorizationService().resolveTokenByUserAndContext(username, scope); - SecurityTokenProvider.instance.set(userToken); - } - catch (ObjectNotFound ex) { - userToken = generateAuthorizationToken(username, scope); - SecurityTokenProvider.instance.set(userToken); - _log.debug("generateAuthorizationToken OK for " + username + " in scope " + scope); - } - catch (Exception e) { - _log.error("Something went wrong in generating token for " + username + " in scope " + scope); - e.printStackTrace(); - } - //_log.trace("Security token set OK for " + username + " in scope " + scope); - } - } - } - getNext().invoke(req, resp); - } - - /** - * - * @param context - * @return true if is the context is syntactically valid - */ - private static boolean validateContext(String context) { - String separator = "/"; - if (!context.matches("\\S+")) - return false; - String[] components=context.split(separator); - if (components.length<2 || components.length>4) - return false; - return true; - } + @Override + public void invoke(Request req, Response resp) throws IOException, ServletException { + SecurityTokenProvider.instance.reset(); + ScopeProvider.instance.reset(); + AuthorizationProvider.instance.reset(); + UmaJWTProvider.instance.reset(); + //_log.trace("SmartGearsPortalValve SecurityTokenProvider and AuthorizationProvider reset OK"); + if (req instanceof HttpServletRequest) { + HttpServletRequest request = (HttpServletRequest) req; + if (!req.getRequestURL().toString().endsWith(LIFERAY_POLLER_CONTEXT)) { //avoid calling gCube auth service for liferay internal poller + PortalContext context = PortalContext.getConfiguration(); + String scope = context.getCurrentScope(request); + String username = getCurrentUsername(request); + if (scope != null && username != null && validateContext(scope)) { + String userToken = null; + try { + ScopeProvider.instance.set(scope); + userToken = authorizationService().resolveTokenByUserAndContext(username, scope); + SecurityTokenProvider.instance.set(userToken); + } catch (ObjectNotFound ex) { + userToken = generateAuthorizationToken(username, scope); + SecurityTokenProvider.instance.set(userToken); + _log.debug("generateAuthorizationToken OK for " + username + " in scope " + scope); + } catch (Exception e) { + _log.error("Something went wrong in generating token for " + username + " in scope " + scope); + e.printStackTrace(); + } + checkUMATicket(request, scope); - /** - * - * @param username - * @param scope - * @throws Exception - */ - private static String generateAuthorizationToken(String username, String scope) { - List userRoles = new ArrayList<>(); - userRoles.add(DEFAULT_ROLE); - String token; - try { - token = authorizationService().generateUserToken(new UserInfo(username, userRoles), scope); - } catch (Exception e) { - e.printStackTrace(); - return null; - } - return token; - } + //_log.trace("Security token set OK for " + username + " in scope " + scope); + } + } + } + getNext().invoke(req, resp); + } - /** - * - * @param httpServletRequest the httpServletRequest object - * @return the instance of the user - * @see GCubeUser - */ - public static String getCurrentUsername(HttpServletRequest httpServletRequest) { - String userIdNo = httpServletRequest.getHeader(PortalContext.USER_ID_ATTR_NAME); - if (userIdNo != null && userIdNo.compareTo("undefined") != 0) { - long userId = -1; - try { - userId = Long.parseLong(userIdNo); - return UserLocalServiceUtil.getUser(userId).getScreenName(); - } catch (NumberFormatException e) { - _log.error("The userId is not a number -> " + userIdNo); - return null; - } catch (Exception e) { - _log.error("The userId does not belong to any user -> " + userIdNo); - return null; - } - } - return null; - } -} + private void checkUMATicket(HttpServletRequest request, String scope) { + HttpSession session = request.getSession(false); + if (session == null) { + if (_log.isDebugEnabled()) { + _log.debug("Session is null"); + } + return; + } + if (_log.isTraceEnabled()) { + _log.trace("Session details: id=" + session.getId() + ", instance=" + session); + } + String urlEncodedScope = null; + try { + urlEncodedScope = URLEncoder.encode(scope, "UTF-8"); + } catch (UnsupportedEncodingException e) { + _log.error("Cannot URL encode scope", e); + return; + } + if (_log.isDebugEnabled()) { + _log.debug("URL encoded scope is: " + urlEncodedScope); + _log.debug("Getting UMA token from session"); + } + JWTToken umaToken = JWTTokenUtil.getUMAFromSession(session); + if (umaToken == null) { + if (_log.isDebugEnabled()) { + _log.debug("UMA token not found in session"); + } + if (_log.isDebugEnabled()) { + _log.debug("Getting current user"); + } + User user = getCurrentUser(request); + if (user == null) { + _log.error("Current user not found, cannot continue"); + return; + } + if (_log.isDebugEnabled()) { + _log.debug("Trying to get UMA token from cache proxy"); + } + umaToken = OIDCTokenProxy.getInstance().getUMAToken(user, session); + if (umaToken == null || !umaToken.getAud().contains(urlEncodedScope)) { + if (umaToken == null) { + if (_log.isDebugEnabled()) { + _log.debug("UMA token is null. Getting new one..."); + } + } else { + _log.info("UMA token for another scope (" + umaToken.getAud() + "). Getting new one for scope: " + + urlEncodedScope); + } + if (_log.isDebugEnabled()) { + _log.debug("Getting OIDC token from session"); + } + JWTToken authToken = JWTTokenUtil.getOIDCFromSession(session); + if (authToken == null) { + if (_log.isDebugEnabled()) { + _log.debug("OIDC token not found in session. Trying to get it from cache proxy"); + } + authToken = getOIDCTokeFromProxyAndSetInSession(user, request, session); + if (authToken == null) { + _log.error("OIDC token is null, cannot continue"); + return; + } else { + if (_log.isDebugEnabled()) { + _log.debug("OIDC token found in cache proxy"); + } + } + } + _log.info("Getting UMA token from OIDC endpoint for scope: " + urlEncodedScope); + OpenIdConnectConfiguration configuration = LiferayOpenIdConnectConfiguration.getConfiguration(request); + try { + // TODO: handle the token expired case and renew it with refresh token. + umaToken = OpenIdConnectRESTHelper.queryUMAToken(configuration.getTokenUrl(), + authToken.getAsBearer(), + urlEncodedScope, null); + } catch (Exception e) { + _log.error("Getting UMA token from server", e); + return; + } + if (_log.isDebugEnabled()) { + _log.debug("Setting UMA token in cache proxy"); + } + OIDCTokenProxy.getInstance().setRPTToken(user, session, umaToken); + if (_log.isDebugEnabled()) { + _log.debug("Setting UMA token in session"); + } + JWTTokenUtil.putUMAInSession(umaToken, session); + } + } + if (_log.isDebugEnabled()) { + _log.debug("Setting UMA token in UMA JWT provider"); + } + UmaJWTProvider.instance.set(umaToken.getRaw()); + } + + private JWTToken getOIDCTokeFromProxyAndSetInSession(User user, HttpServletRequest request, HttpSession session) { + JWTToken token = OIDCTokenProxy.getInstance().getOIDCToken(user, session); + if (token == null) { + _log.warn("OIDC token is null also in cache proxy!"); + } else { + if (_log.isDebugEnabled()) { + _log.debug("Setting OIDC token took from cache proxy in session"); + } + JWTTokenUtil.putOIDCInSession(token, session); + } + return token; + } + + /** + * + * @param context + * @return true if is the context is syntactically valid + */ + private static boolean validateContext(String context) { + String separator = "/"; + if (!context.matches("\\S+")) + return false; + String[] components = context.split(separator); + if (components.length < 2 || components.length > 4) + return false; + return true; + } + /** + * + * @param username + * @param scope + * @throws Exception + */ + private static String generateAuthorizationToken(String username, String scope) { + List userRoles = new ArrayList<>(); + userRoles.add(DEFAULT_ROLE); + String token; + try { + token = authorizationService().generateUserToken(new UserInfo(username, userRoles), scope); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + return token; + } + + /** + * + * @param httpServletRequest the httpServletRequest object + * @return the instance of the user + * @see GCubeUser + */ + public static String getCurrentUsername(HttpServletRequest httpServletRequest) { + User user = getCurrentUser(httpServletRequest); + return user != null ? user.getScreenName() : null; + } + + public static User getCurrentUser(HttpServletRequest httpServletRequest) { + String userIdNo = httpServletRequest.getHeader(PortalContext.USER_ID_ATTR_NAME); + if (userIdNo != null && userIdNo.compareTo("undefined") != 0) { + long userId = -1; + try { + userId = Long.parseLong(userIdNo); + return UserLocalServiceUtil.getUser(userId); + } catch (NumberFormatException e) { + _log.error("The userId is not a number -> " + userIdNo); + return null; + } catch (Exception e) { + _log.error("The userId does not belong to any user -> " + userIdNo); + return null; + } + } + return null; + } +}