/** * */ package org.gcube.portlets.widgets.wsthreddssync.server; import java.util.List; import java.util.Map; import javax.servlet.http.HttpServletRequest; import org.gcube.common.authorization.library.provider.AccessTokenProvider; import org.gcube.common.authorization.library.provider.SecurityTokenProvider; import org.gcube.common.portal.PortalContext; import org.gcube.common.scope.api.ScopeProvider; import org.gcube.oidc.rest.JWTToken; import org.gcube.portal.oidc.lr62.JWTTokenUtil; import org.gcube.portal.oidc.lr62.OIDCUmaUtil; import org.gcube.portal.wssynclibrary.shared.ItemNotSynched; import org.gcube.portal.wssynclibrary.shared.WorkspaceFolderLocked; import org.gcube.portal.wssynclibrary.shared.thredds.ThCatalogueBean; import org.gcube.portal.wssynclibrary.shared.thredds.ThSyncFolderDescriptor; import org.gcube.portal.wssynclibrary.shared.thredds.ThSyncStatus; import org.gcube.portal.wssynclibrary.shared.thredds.ThSynchFolderConfiguration; import org.gcube.portal.wssynclibrary.thredds.WorkspaceThreddsSynchronize; import org.gcube.usecases.ws.thredds.SyncEngine; import org.gcube.usecases.ws.thredds.faults.WorkspaceNotSynchedException; import org.gcube.usecases.ws.thredds.model.ContainerType; import org.gcube.vomanagement.usermanagement.model.GCubeUser; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * The Class SyncronizeWithThredds. * * @author Francesco Mangiacrapa francesco.mangiacrapa@isti.cnr.it Feb 7, 2018 * updated May, 2021 */ public class SyncronizeWithThredds { /** The logger. */ private Logger logger = LoggerFactory.getLogger(SyncronizeWithThredds.class); /** The workspace thredds synchronize. */ private WorkspaceThreddsSynchronize workspaceThreddsSynchronizeLib; /** The Constant sdf. */ // private static final SimpleDateFormat sdf = new // SimpleDateFormat("yyyy.MM.dd.HH.mm.ss"); /** * Instantiates a new publish on thredds. * * @param wsScopeUserToken the ws scope user token * @param username the username * @param httpSession the http session */ public SyncronizeWithThredds() { this.workspaceThreddsSynchronizeLib = WorkspaceThreddsSynchronize.getInstance(); } /** * Sets the context parameters. * * @param scope the scope * @param userToken the user token */ private void setContextParameters(String scope, String userToken) { logger.debug("Setting context parameters with scope: " + scope + ", user token: " + userToken.substring(0, 10) + "-MASKED-TOKEN"); ScopeProvider.instance.set(scope); SecurityTokenProvider.instance.set(userToken); } /** * Change context for UMA. * * @param httpRequest the http request * @param user the user * @param targetScope the target scope * @return the string representing the previous UMA Token read from * {@link UmaJWTProvider#get()} */ private String changeContextForUMA(HttpServletRequest httpRequest, GCubeUser user, String targetScope) { logger.debug("Setting UMA context with target scope: " + targetScope + ", user: " + user.getUsername()); String previousUMAToken = null; try { previousUMAToken = AccessTokenProvider.instance.get(); JWTToken umaToken = OIDCUmaUtil.getUMAToken(httpRequest, user.getUsername(), targetScope); if (umaToken == null) { logger.info("Uma Token is null or empty, skipping operation and returning null"); return null; } String subAccessToken = umaToken.getAccessTokenString().substring(0, 10); logger.info("Going to set UMA Token: " + subAccessToken + "-MASKED-TOKEN"); // UmaJWTProvider.instance.set(umaToken); AccessTokenProvider.instance.set(JWTTokenUtil.getAccessTokenString(umaToken)); logger.debug("UmaJWTProvider instance set performed to : " + subAccessToken + "-MASKED-TOKEN"); } catch (Exception e) { logger.warn("Error on set context for UMA: ", e); if (previousUMAToken != null) { logger.info("Setting previous UMA Token: " + previousUMAToken.substring(0, 10) + "-MASKED-TOKEN"); AccessTokenProvider.instance.set(previousUMAToken); } } return previousUMAToken; } /** * Checks if is item synched. * * @param folderId the folder id * @param scope the scope * @param userToken the user token * @param itemProperties the item properties * @param itemType the item type * @return true, if is item synched * @throws ItemNotSynched the item not synched * @throws Exception the exception */ public boolean isItemSynched(String folderId, String scope, String userToken, Map itemProperties, ContainerType itemType) throws ItemNotSynched, Exception { setContextParameters(scope, userToken); return workspaceThreddsSynchronizeLib.isItemSynched(folderId, itemProperties, itemType); } /** * Gets the configuration. * * @param folderId the folder id * @param loadStatus the load status. If true it loads the status by calling * the {@link SyncEngine#check(String, boolean)} but it is * time consuming. Otherwise it calls the * {@link SyncEngine#getConfig(String)} without sync status * @param httpRequest the http request * @param user the user * @return the configuration * @throws ItemNotSynched the item not synched * @throws Exception the exception */ public ThSyncFolderDescriptor getConfiguration(String folderId, boolean loadStatus, HttpServletRequest httpRequest, GCubeUser user) throws ItemNotSynched, Exception { ThSyncFolderDescriptor config = null; String wsScope = PortalContext.getConfiguration().getCurrentScope(httpRequest); String wsUserToken = PortalContext.getConfiguration().getCurrentUserToken(wsScope, user.getUsername()); String originalScope = wsScope; String originalToken = wsUserToken; String previousUmaToken = null; String targetScope = null; try { setContextParameters(wsScope, wsUserToken); if (loadStatus) { config = workspaceThreddsSynchronizeLib.getConfiguration(folderId); // context switch for UMA token targetScope = config.getConfiguration().getTargetContext(); previousUmaToken = changeContextForUMA(httpRequest, user, targetScope); // context switch for gcube-token and scope // getting token into target scope String targetScopeUserToken = PortalContext.getConfiguration().getCurrentUserToken(targetScope, user.getUsername()); setContextParameters(targetScope, targetScopeUserToken); config = workspaceThreddsSynchronizeLib.checkItemSynched(folderId); } else { config = workspaceThreddsSynchronizeLib.getConfiguration(folderId); } } catch (WorkspaceNotSynchedException e) { logger.debug("WorkspaceNotSynchedException catched. The item with id: " + folderId + " is not synched"); } catch (WorkspaceFolderLocked e1) { logger.info("The folder with id: " + folderId + " is locked"); throw new Exception( "The folder with id: " + folderId + " is currently locked. Another sync process is in progress"); } catch (Exception e) { logger.error("Error on reading the configuration for id: " + folderId, e); throw e; } finally { if (previousUmaToken != null) { // resetting UMA token in the WS scope AccessTokenProvider.instance.set(previousUmaToken); } if (originalScope != null && targetScope != null && originalScope.compareTo(targetScope) != 0) { logger.info("Resetting the scope: " + originalScope + " which was original WS context"); ScopeProvider.instance.set(originalScope); if (originalToken != null) { logger.info("Resetting the user token: " + originalToken.substring(0, 10) + "-MASKED-TOKEN which was original WS context"); SecurityTokenProvider.instance.set(originalToken); } } } return config; } /** * Gets the available catalogues. * * @param httpRequest the http request * @param user the user * @param targetScope the target scope * @return the available catalogues * @throws Exception the exception */ public List getAvailableCatalogues(HttpServletRequest httpRequest, GCubeUser user, String targetScope) throws Exception { String originalScope = null; String originalToken = null; String previousUmaToken = null; List listCatalogues = null; try { // context switch for Uma token previousUmaToken = changeContextForUMA(httpRequest, user, targetScope); // context switch for gcube-token and scope PortalContext pConfig = PortalContext.getConfiguration(); String wsScope = pConfig.getCurrentScope(httpRequest); String wsUserToken = pConfig.getCurrentUserToken(wsScope, user.getUsername()); // Thread Local contexts originalScope = wsScope; originalToken = wsUserToken; // getting token into target scope String targetScopeUserToken = PortalContext.getConfiguration().getCurrentUserToken(targetScope, user.getUsername()); setContextParameters(targetScope, targetScopeUserToken); // calling the engine listCatalogues = workspaceThreddsSynchronizeLib.getAvailableCatalogues(); } catch (Exception e) { logger.error("Error on getting available Catalogues in the scope: " + targetScope, e); } finally { if (previousUmaToken != null) { // resetting UMA token in the WS scope AccessTokenProvider.instance.set(previousUmaToken); } if (originalScope != null && originalScope.compareTo(targetScope) != 0) { logger.info("Resetting the scope: " + originalScope + " which was original WS context"); ScopeProvider.instance.set(originalScope); if (originalToken != null) { logger.info("Resetting the user token: " + originalToken.substring(0, 10) + "-MASKED-TOKEN which was original WS context"); SecurityTokenProvider.instance.set(originalToken); } } } return listCatalogues; } /** * Do sync folder. * * @param folderId the folder id * @param thConfig the th config * @param httpRequest the http request * @param user the user * @return the th sync status * @throws Exception the exception */ public synchronized ThSyncStatus doSyncFolder(final String folderId, ThSynchFolderConfiguration thConfig, HttpServletRequest httpRequest, GCubeUser user) throws Exception { logger.debug("called doSynFolder for folderId: " + folderId); boolean firstSync = false; String originalScope = null; String originalToken = null; String previousUmaToken = null; if (thConfig == null) { throw new Exception("A valid folder configuration must be provided to perform the synchronization"); } String targetScope = thConfig.getTargetContext(); if (targetScope == null || targetScope.isEmpty()) { throw new Exception("Error, the target scope is not valid!"); } logger.info("going to doSynFolder for folderId: " + folderId + ", target scope is: " + targetScope); try { // context switch for Uma token previousUmaToken = changeContextForUMA(httpRequest, user, targetScope); // context switch for gcube-token and scope PortalContext pConfig = PortalContext.getConfiguration(); String wsScope = pConfig.getCurrentScope(httpRequest); String wsUserToken = pConfig.getCurrentUserToken(wsScope, user.getUsername()); // Thread Local contexts originalScope = wsScope; originalToken = wsUserToken; // getting token into target scope String targetScopeUserToken = PortalContext.getConfiguration().getCurrentUserToken(targetScope, user.getUsername()); setContextParameters(targetScope, targetScopeUserToken); ThSyncFolderDescriptor folder = workspaceThreddsSynchronizeLib.checkItemSynched(folderId); } catch (ItemNotSynched e) { firstSync = true; } catch (Exception e) { logger.error("Error on check item sync: ", e); throw e; } try { if (firstSync) { logger.info("First sync setting the synchronized folder configuration: " + thConfig); workspaceThreddsSynchronizeLib.setSynchronizedFolder(thConfig, folderId); } logger.info("Calling do sync on folder id: " + folderId); return workspaceThreddsSynchronizeLib.doSync(folderId); } catch (Exception e) { logger.error("Error on doSyncFolder for folderId: " + folderId, e); throw e; } finally { if (previousUmaToken != null) { // resetting UMA token in the WS scope AccessTokenProvider.instance.set(previousUmaToken); } if (originalScope != null && originalScope.compareTo(targetScope) != 0) { logger.info("Resetting the scope: " + originalScope + " which was original WS context"); ScopeProvider.instance.set(originalScope); if (originalToken != null) { logger.info("Resetting the user token: " + originalToken.substring(0, 10) + "-MASKED-TOKEN which was original WS context"); SecurityTokenProvider.instance.set(originalToken); } } } } /** * Do un sync. * * @param folderId the folder id * @param deleteRemoteContent the delete remote content * @param thConfig the th config * @param httpRequest the http request * @param user the user * @return the boolean * @throws Exception the exception */ public Boolean doUnSync(String folderId, boolean deleteRemoteContent, ThSynchFolderConfiguration thConfig, HttpServletRequest httpRequest, GCubeUser user) throws Exception { logger.debug("called doUnSync for folderId: " + folderId); String originalScope = null; String originalToken = null; String previousUmaToken = null; if (thConfig == null) { throw new Exception("A valid folder configuration must be provided to perform the synchronization"); } String targetScope = thConfig.getTargetContext(); if (targetScope == null || targetScope.isEmpty()) { throw new Exception("Error, the target scope is not valid!"); } logger.info("going to doSynFolder for folderId: " + folderId + ", target scope is: " + targetScope); try { // context switch for Uma token previousUmaToken = changeContextForUMA(httpRequest, user, targetScope); // context switch for gcube-token and scope PortalContext pConfig = PortalContext.getConfiguration(); String wsScope = pConfig.getCurrentScope(httpRequest); String wsUserToken = pConfig.getCurrentUserToken(wsScope, user.getUsername()); // Thread Local contexts originalScope = wsScope; originalToken = wsUserToken; // getting token into target scope String targetScopeUserToken = PortalContext.getConfiguration().getCurrentUserToken(targetScope, user.getUsername()); setContextParameters(targetScope, targetScopeUserToken); return workspaceThreddsSynchronizeLib.doUnSync(folderId, deleteRemoteContent); } catch (ItemNotSynched e) { throw new Exception("The item with id: " + folderId + " is not synched"); } catch (Exception e) { logger.error("Error on check item sync: ", e); throw new Exception("Sorry an error occurred during folder publishing, refresh and try again"); } finally { if (previousUmaToken != null) { // resetting UMA token in the WS scope AccessTokenProvider.instance.set(previousUmaToken); } if (originalScope != null && originalScope.compareTo(targetScope) != 0) { logger.info("Resetting the scope: " + originalScope + " which was original WS context"); ScopeProvider.instance.set(originalScope); if (originalToken != null) { logger.info("Resetting the user token: " + originalToken.substring(0, 10) + "-MASKED-TOKEN which was original WS context"); SecurityTokenProvider.instance.set(originalToken); } } } } /** * Gets the sync status. * * @param itemId the item id * @param scope the scope * @param userToken the user token * @return the sync status * @throws ItemNotSynched the item not synched * @throws Exception the exception */ public ThSyncStatus monitorSyncStatus(String itemId, String scope, String userToken) throws ItemNotSynched, Exception { setContextParameters(scope, userToken); return workspaceThreddsSynchronizeLib.monitorSyncStatus(itemId); } /** * Register callback for id. * * @param folderId the folder id * @param scope the scope * @param userToken the user token * @throws Exception the exception */ protected void registerCallbackForId(String folderId, String scope, String userToken) throws Exception { setContextParameters(scope, userToken); workspaceThreddsSynchronizeLib.registerCallbackForId(folderId); } }