@ -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 < String > 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 < String > 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 ;
}
}