2021-09-17 17:34:22 +02:00
package org.gcube.portlets.user.bluecloud ;
2021-09-22 17:49:40 +02:00
import static org.gcube.resources.discovery.icclient.ICFactory.clientFor ;
import static org.gcube.resources.discovery.icclient.ICFactory.queryFor ;
2021-09-17 17:34:22 +02:00
import java.io.BufferedReader ;
import java.io.IOException ;
import java.io.InputStreamReader ;
import java.io.OutputStream ;
import java.net.HttpURLConnection ;
import java.net.URL ;
2021-09-22 17:49:40 +02:00
import java.util.List ;
2021-09-17 17:34:22 +02:00
import javax.portlet.PortletException ;
import javax.portlet.PortletRequestDispatcher ;
import javax.portlet.RenderRequest ;
import javax.portlet.RenderResponse ;
import javax.servlet.http.HttpServletRequest ;
2021-09-22 17:49:40 +02:00
import org.gcube.common.encryption.encrypter.StringEncrypter ;
2021-09-17 17:34:22 +02:00
import org.gcube.common.portal.PortalContext ;
2021-09-22 17:49:40 +02:00
import org.gcube.common.resources.gcore.ServiceEndpoint ;
import org.gcube.common.resources.gcore.ServiceEndpoint.AccessPoint ;
import org.gcube.common.scope.api.ScopeProvider ;
2021-09-17 17:34:22 +02:00
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.LiferayOpenIdConnectConfiguration ;
import org.gcube.portal.oidc.lr62.OIDCUmaUtil ;
2021-09-22 17:49:40 +02:00
import org.gcube.resources.discovery.client.api.DiscoveryClient ;
import org.gcube.resources.discovery.client.queries.api.SimpleQuery ;
2021-09-17 17:34:22 +02:00
import org.gcube.vomanagement.usermanagement.impl.LiferayUserManager ;
import org.gcube.vomanagement.usermanagement.model.GCubeUser ;
import com.google.gson.Gson ;
import com.google.gson.JsonElement ;
import com.google.gson.JsonObject ;
import com.google.gson.JsonParser ;
import com.liferay.portal.kernel.log.LogFactoryUtil ;
import com.liferay.portal.kernel.util.ParamUtil ;
import com.liferay.portal.kernel.util.PrefsPropsUtil ;
import com.liferay.portal.util.PortalUtil ;
import com.liferay.util.bridges.mvc.MVCPortlet ;
import net.spy.memcached.MemcachedClient ;
/ * *
* Portlet implementation class DdasVreIntegration
* /
public class DdasVreIntegration extends MVCPortlet {
private static com . liferay . portal . kernel . log . Log log = LogFactoryUtil . getLog ( DdasVreIntegration . class ) ;
2021-09-22 17:49:40 +02:00
private static final String BC_BROKER_RESOURCE_NAME = " Blue-Cloud-DataDownloadAndAccess " ;
private static final String CATEGORY_NAME = " Service " ;
private static final String BROKER_LOGIN_PROPERTY_NAME = " login " ;
private static final String BROKER_FEEDBACK_PROPERTY_NAME = " download-ready " ;
2021-09-17 17:34:22 +02:00
public static final String CONDUCTOR_WORKFLOW_NAME = " da_cache_to_shub " ;
private static final int CONNECTION_TIMEOUT = 10000 ;
private static final int READ_TIMEOUT = 15000 ;
private static String ENCODED_OTP_PARAM = " b3Rw " ;
private static final String OPERATION_ERROR = " /html/error_pages/operation-error.jsp " ;
2021-09-17 18:10:22 +02:00
private static final String TOKEN_EXPIRED = " /html/error_pages/bc-token-expired.jsp " ;
2021-09-17 17:34:22 +02:00
/ * *
*
* /
public void render ( RenderRequest renderRequest , RenderResponse response ) throws PortletException , IOException {
HttpServletRequest httpReq = PortalUtil . getOriginalServletRequest ( PortalUtil . getHttpServletRequest ( renderRequest ) ) ;
String [ ] params = ParamUtil . getParameterValues ( httpReq , ENCODED_OTP_PARAM ) ;
boolean result = false ;
if ( params ! = null & & params . length > 0 ) {
String otp = params [ 0 ] ;
2021-10-18 12:45:13 +02:00
log . debug ( " Read Blue-Cloud cache OTP= " + otp ) ;
2021-09-17 17:34:22 +02:00
if ( otp ! = null & & ! otp . equals ( " " ) ) {
JsonElement brokerRequest = checkRequest ( otp ) ;
2021-09-17 18:10:22 +02:00
if ( brokerRequest = = null ) { //the token is expired or there was no request, return to the JSP token expired page
PortletRequestDispatcher dispatcher = getPortletContext ( ) . getRequestDispatcher ( TOKEN_EXPIRED ) ;
dispatcher . include ( renderRequest , response ) ;
return ;
}
2021-09-17 17:34:22 +02:00
GCubeUser currentUser = getCurrentUser ( renderRequest ) ;
log . debug ( " current user is + " + currentUser . getUsername ( ) + " sending request to conductor ... " ) ;
try {
2021-09-24 15:48:47 +02:00
Integer userOrderNumber = brokerRequest . getAsJsonObject ( ) . get ( " orderNumber " ) . getAsInt ( ) ;
String userOrderName = brokerRequest . getAsJsonObject ( ) . get ( " userOrderName " ) . getAsString ( ) ;
renderRequest . setAttribute ( " orderData " , userOrderName + " , order number " + userOrderNumber ) ; //pass to the JSP
2021-09-17 17:34:22 +02:00
result = sendRequestToConductor ( brokerRequest , currentUser , renderRequest ) ;
} catch ( Exception e ) {
// TODO Auto-generated catch block
e . printStackTrace ( ) ;
}
renderRequest . setAttribute ( " theResult " , result ) ; //pass to the JSP
if ( ! result ) { //redirect to error page
PortletRequestDispatcher dispatcher = getPortletContext ( ) . getRequestDispatcher ( OPERATION_ERROR ) ;
dispatcher . include ( renderRequest , response ) ;
2021-09-17 18:10:22 +02:00
return ;
2021-09-17 17:34:22 +02:00
}
2021-09-17 18:10:22 +02:00
} else {
//the token is missing in the request URL
PortletRequestDispatcher dispatcher = getPortletContext ( ) . getRequestDispatcher ( TOKEN_EXPIRED ) ;
dispatcher . include ( renderRequest , response ) ;
return ;
2021-09-17 17:34:22 +02:00
}
}
response . setContentType ( " text/html " ) ;
PortletRequestDispatcher dispatcher = getPortletContext ( ) . getRequestDispatcher ( " /html/ddasvreintegration/view.jsp " ) ;
dispatcher . include ( renderRequest , response ) ;
}
/ * *
*
* @param brokerRequest
* @param currentUser
* @param renderRequest
* @return
* @throws Exception
* /
private boolean sendRequestToConductor ( JsonElement brokerRequest , GCubeUser currentUser , RenderRequest renderRequest ) throws Exception {
2021-09-22 17:49:40 +02:00
2021-09-17 17:34:22 +02:00
String userEmail = currentUser . getEmail ( ) ;
log . debug ( " current user email is + " + userEmail ) ;
2021-09-22 17:49:40 +02:00
Callback theCallback = getVRECallbackURLFromServiceEndpoint ( userEmail ) ;
2021-09-17 17:34:22 +02:00
HttpServletRequest httpReq = PortalUtil . getOriginalServletRequest ( PortalUtil . getHttpServletRequest ( renderRequest ) ) ;
String context = " / " + PortalContext . getConfiguration ( ) . getInfrastructureName ( ) ;
JWTToken userUMAToken = OIDCUmaUtil . getUMAToken ( httpReq , currentUser . getUsername ( ) , context ) ;
2021-09-22 17:49:40 +02:00
ConductorRequestBean req = new ConductorRequestBean ( userUMAToken . getAccessTokenString ( ) , theCallback ) ;
2021-09-17 17:34:22 +02:00
Gson gson = new Gson ( ) ;
JsonElement jsonElement = gson . toJsonTree ( req ) ;
jsonElement . getAsJsonObject ( ) . add ( " descriptor " , brokerRequest ) ; //append the original request as descriptor object
2021-09-22 17:49:40 +02:00
log . debug ( " \ n \ nsendRequestToConductor \ n " + jsonElement . toString ( ) ) ;
2021-09-17 17:34:22 +02:00
URL eventPublisherURL = new URL ( PrefsPropsUtil . getString ( PortalUtil . getDefaultCompanyId ( ) , " d4science.event-broker-endpoint " ) ) ;
return doPost ( jsonElement , new URL ( eventPublisherURL + CONDUCTOR_WORKFLOW_NAME ) ) ;
}
/ * *
*
* @param payload
* @param endpoint
* @return
* /
private boolean doPost ( JsonElement payload , URL endpoint ) {
try {
OpenIdConnectConfiguration openIdConnectConfiguration = LiferayOpenIdConnectConfiguration . getConfiguration ( ) ;
String clientId = openIdConnectConfiguration . getPortalClientId ( ) ;
log . debug ( " Getting auth token for client '{}' if needed " + clientId ) ;
JWTToken token = getAuthorizationToken ( openIdConnectConfiguration ) ;
log . debug ( " Performing HTTP POST to: {} " + endpoint ) ;
HttpURLConnection connection = ( HttpURLConnection ) endpoint . openConnection ( ) ;
connection . setRequestMethod ( " POST " ) ;
connection . setConnectTimeout ( CONNECTION_TIMEOUT ) ;
log . trace ( " HTTP connection timeout set to: " + connection . getConnectTimeout ( ) ) ;
connection . setReadTimeout ( READ_TIMEOUT ) ;
log . trace ( " HTTP connection Read timeout set to: " + connection . getReadTimeout ( ) ) ;
connection . setRequestProperty ( " Content-Type " , " application/json " ) ;
// Commented out as per the Conductor issue: https://github.com/Netflix/conductor/issues/376
// connection.setRequestProperty("Accept", "application/json");
connection . setDoOutput ( true ) ;
if ( token ! = null ) {
log . debug ( " Setting authorization header as: " + token . getAccessTokenAsBearer ( ) ) ;
connection . setRequestProperty ( " Authorization " , token . getAccessTokenAsBearer ( ) ) ;
} else {
log . debug ( " Sending request without authorization header " ) ;
}
OutputStream os = connection . getOutputStream ( ) ;
String jsonString = payload . toString ( ) ;
log . trace ( " Sending event JSON: " + jsonString ) ;
os . write ( jsonString . getBytes ( " UTF-8 " ) ) ;
os . flush ( ) ;
os . close ( ) ;
2021-09-24 15:48:47 +02:00
2021-09-17 17:34:22 +02:00
StringBuilder sb = new StringBuilder ( ) ;
2021-09-24 15:48:47 +02:00
boolean ok = true ;
int httpResultCode = - 1 ;
try {
httpResultCode = connection . getResponseCode ( ) ;
2021-09-17 17:34:22 +02:00
log . trace ( " HTTP Response code: " + httpResultCode ) ;
log . trace ( " Reading response " ) ;
2021-09-24 15:48:47 +02:00
2021-09-17 17:34:22 +02:00
InputStreamReader isr = null ;
if ( httpResultCode = = HttpURLConnection . HTTP_OK ) {
isr = new InputStreamReader ( connection . getInputStream ( ) , " UTF-8 " ) ;
} else {
ok = false ;
isr = new InputStreamReader ( connection . getErrorStream ( ) , " UTF-8 " ) ;
}
BufferedReader br = new BufferedReader ( isr ) ;
String line = null ;
while ( ( line = br . readLine ( ) ) ! = null ) {
sb . append ( line + " \ n " ) ;
}
br . close ( ) ;
isr . close ( ) ;
2021-09-24 15:48:47 +02:00
} catch ( java . net . SocketTimeoutException e ) {
log . warn ( " Read timout may have occurred, however I think it went through anyways. " ) ;
return true ;
}
2021-09-17 17:34:22 +02:00
if ( ok ) {
log . info ( " Post to Conductor is OK " + httpResultCode ) ;
log . trace ( " Response message from server: {} " + sb . toString ( ) ) ;
return true ;
} else {
log . info ( " Post to Conductor is NOT OK " + httpResultCode ) ;
log . trace ( " Response message from server: {} " + sb . toString ( ) ) ;
return false ;
}
} catch ( Exception e ) {
log . error ( " POSTing JSON to: " + endpoint , e ) ;
return false ;
}
}
protected JWTToken getAuthorizationToken ( OpenIdConnectConfiguration openIdConnectConfiguration ) throws OpenIdConnectRESTHelperException {
String clientId = openIdConnectConfiguration . getPortalClientId ( ) ;
String clientSecret = openIdConnectConfiguration . getPortalClientSecret ( ) ;
URL tokenURL = openIdConnectConfiguration . getTokenURL ( ) ;
if ( clientId ! = null & & clientSecret ! = null & & tokenURL ! = null ) {
log . debug ( " Getting OIDC token for clientId " + clientId + " from: " + tokenURL ) ;
return OpenIdConnectRESTHelper . queryClientToken ( clientId , clientSecret , tokenURL ) ;
} else {
log . debug ( " Can't get OIDC token since not all the required params were provied " ) ;
return null ;
}
}
/ * *
* Check request parameters
* @param clientId
* @param clientSecret
* @param redirectUri
* @param code
* @param grantType
* @return see https : //tools.ietf.org/html/rfc6749#section-5.2
* /
private JsonElement checkRequest ( String code ) {
try {
MemcachedClient entries = new DistributedCacheClient ( ) . getMemcachedClient ( ) ;
if ( entries . get ( code ) = = null ) {
log . error ( " Could not find OTP key in memcache " ) ;
return null ;
}
log . debug ( " Got temp code (OTP) and looking into memcached for correspondance, " + code ) ;
String brokerRequest = ( String ) entries . get ( code ) ;
System . out . println ( " \ n \ nbrokerRequest= " + brokerRequest ) ;
JsonElement jelement = new JsonParser ( ) . parse ( brokerRequest ) ;
JsonObject jobject = jelement . getAsJsonObject ( ) ;
log . debug ( " Found tempCode into memcached, broker request= " + jobject . toString ( ) ) ;
2021-10-18 12:45:13 +02:00
log . debug ( " Invalidating tempCode into memcached with key= " + code ) ;
entries . delete ( code ) ;
log . debug ( " Invalidated key " + code + " with success. " ) ;
2021-09-17 17:34:22 +02:00
return jelement ;
} catch ( Exception e ) {
log . error ( " Failed to check the correctness of the broker request " , e ) ;
return null ;
}
}
public static GCubeUser getCurrentUser ( RenderRequest request ) {
long userId ;
try {
userId = PortalUtil . getUser ( request ) . getUserId ( ) ;
return getCurrentUser ( userId ) ;
} catch ( Exception e ) {
e . printStackTrace ( ) ;
}
return null ;
}
public static GCubeUser getCurrentUser ( long userId ) {
try {
return new LiferayUserManager ( ) . getUserById ( userId ) ;
} catch ( Exception e ) {
e . printStackTrace ( ) ;
}
return null ;
}
@SuppressWarnings ( " unused " )
private class Callback {
2021-09-22 17:49:40 +02:00
URL url ; // BROKER_FEEDBACK_ENDPOINT
String email ; //where to send the email with the result report
String username ; //the username to use to login using bearer token method
String password ; //the password to use to login using bearer token method
URL authorize_url ; //the endpoint to login and start the oAuth2 Bearer Token flow with the above credentials
public Callback ( URL url , String email , String username , String password , URL authorize_url ) {
2021-09-17 17:34:22 +02:00
super ( ) ;
this . url = url ;
this . email = email ;
2021-09-22 17:49:40 +02:00
this . username = username ;
this . password = password ;
this . authorize_url = authorize_url ;
2021-09-17 17:34:22 +02:00
}
}
@SuppressWarnings ( " unused " )
private class ConductorRequestBean {
String token ;
Callback callback ;
public ConductorRequestBean ( String token , Callback callback ) {
super ( ) ;
this . token = token ;
this . callback = callback ;
}
}
2021-09-22 17:49:40 +02:00
/ * *
* Retrieve the vre callbac URL to pass to the broker service in the response from the endpoint resource on IS
* @throws Exception
* /
private Callback getVRECallbackURLFromServiceEndpoint ( String userEmail ) {
String currentScope = ScopeProvider . instance . get ( ) ;
String infrastructure = " / " + PortalContext . getConfiguration ( ) . getInfrastructureName ( ) ;
ScopeProvider . instance . set ( infrastructure ) ;
try {
SimpleQuery query = queryFor ( ServiceEndpoint . class ) ;
query . addCondition ( " $resource/Profile/Name/text() eq ' " + BC_BROKER_RESOURCE_NAME + " ' " ) ;
query . addCondition ( " $resource/Profile/Category/text() eq ' " + CATEGORY_NAME + " ' " ) ;
DiscoveryClient < ServiceEndpoint > client = clientFor ( ServiceEndpoint . class ) ;
try {
List < ServiceEndpoint > list = client . submit ( query ) ;
if ( list . size ( ) > 1 ) {
System . out . println ( " Too many Service Endpoints having name " + BC_BROKER_RESOURCE_NAME + " in this scope having Category " + CATEGORY_NAME ) ;
}
else if ( list . size ( ) = = 0 ) {
System . out . println ( " There is no Service Endpoint having name " + BC_BROKER_RESOURCE_NAME + " and Category " + CATEGORY_NAME + " in this context " + infrastructure ) ;
}
else {
for ( ServiceEndpoint res : list ) {
AccessPoint [ ] accessPoints = ( AccessPoint [ ] ) res . profile ( ) . accessPoints ( ) . toArray ( new AccessPoint [ res . profile ( ) . accessPoints ( ) . size ( ) ] ) ;
String username = null ;
String password = null ;
String brokerApiURL = null ;
String brokerfeedbackEndpoint = null ;
String brokerLoginEndpoint = null ;
for ( AccessPoint found : accessPoints ) {
brokerApiURL = found . address ( ) ;
log . debug ( " \ nBC DD&AS AccessPoint found URL: " + brokerApiURL ) ;
username = found . username ( ) ;
String passEncrypted = found . password ( ) ;
password = StringEncrypter . getEncrypter ( ) . decrypt ( passEncrypted ) ;
for ( ServiceEndpoint . Property prop : found . properties ( ) ) {
if ( prop . name ( ) . compareTo ( BROKER_FEEDBACK_PROPERTY_NAME ) = = 0 )
brokerfeedbackEndpoint = prop . value ( ) ;
if ( prop . name ( ) . compareTo ( BROKER_LOGIN_PROPERTY_NAME ) = = 0 )
brokerLoginEndpoint = prop . value ( ) ;
}
}
String brokerAPILoginURL = brokerApiURL + brokerLoginEndpoint ; //https://data.blue-cloud.org/api + /login
String brokerFeedbackURL = brokerApiURL + brokerfeedbackEndpoint ; //https://data.blue-cloud.org/api + /vre/download-ready
Callback toReturn = new Callback ( new URL ( brokerFeedbackURL ) , userEmail , username , password , new URL ( brokerAPILoginURL ) ) ;
return toReturn ;
}
}
} catch ( Exception e ) {
e . printStackTrace ( ) ;
}
} catch ( Exception e ) {
log . error ( " There is no Service Endpoint having name: " + BC_BROKER_RESOURCE_NAME + " and Category " + CATEGORY_NAME + " on root context " ) ;
} finally {
ScopeProvider . instance . set ( currentScope ) ;
}
ScopeProvider . instance . set ( currentScope ) ;
return null ;
}
2021-09-17 17:34:22 +02:00
}