package org.gcube.portlets.gcubeckan.gcubeckandatacatalog.server; import static org.gcube.common.authorization.client.Constants.authorizationService; import java.io.IOException; import java.io.InputStream; import java.net.HttpURLConnection; import java.net.URL; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import org.apache.commons.codec.binary.Base64; import org.apache.http.HttpStatus; import org.gcube.application.framework.core.session.ASLSession; import org.gcube.application.framework.core.session.SessionManager; import org.gcube.common.scope.api.ScopeProvider; import org.gcube.datacatalogue.ckanutillibrary.CKanUtilsImpl; import org.gcube.datacatalogue.ckanutillibrary.models.RolesIntoOrganization; import org.gcube.portal.custom.scopemanager.scopehelper.ScopeHelper; import org.gcube.portlets.gcubeckan.gcubeckandatacatalog.client.GcubeCkanDataCatalogService; import org.gcube.portlets.gcubeckan.gcubeckandatacatalog.shared.CkanConnectorAccessPoint; import org.gcube.portlets.gcubeckan.gcubeckandatacatalog.shared.CkanRole; import org.gcube.vomanagement.usermanagement.GroupManager; import org.gcube.vomanagement.usermanagement.RoleManager; import org.gcube.vomanagement.usermanagement.UserManager; import org.gcube.vomanagement.usermanagement.impl.LiferayGroupManager; import org.gcube.vomanagement.usermanagement.impl.LiferayRoleManager; import org.gcube.vomanagement.usermanagement.impl.LiferayUserManager; import org.gcube.vomanagement.usermanagement.model.GCubeRole; import org.gcube.vomanagement.usermanagement.model.GatewayRolesNames; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.gwt.user.server.rpc.RemoteServiceServlet; import eu.trentorise.opendata.jackan.model.CkanOrganization; import eu.trentorise.opendata.traceprov.internal.org.apache.commons.io.IOUtils; /** * The server side implementation of the RPC service. * * @author Francesco Mangiacrapa francesco.mangiacrapa@isti.cnr.it * Jun 10, 2016 */ @SuppressWarnings("serial") public class GcubeCkanDataCatalogServiceImpl extends RemoteServiceServlet implements GcubeCkanDataCatalogService { /** * */ public static final String UTF_8 = "UTF-8"; private static final String PORT_HTTP = ":80"; private static final String PORT_HTTPS = ":443"; private static final String HTTPS = "https"; private static final String HTTP = "http"; public static String CKANCONNECTORCONTEXT = "CkanConnectorContext"; // public static String CKANHIDEHEADER = "CkanHideHeader"; public static String CKANCONNECTORLOGOUT = "CkanConnectorLogout"; public static final String USERNAME_ATTRIBUTE = ScopeHelper.USERNAME_ATTRIBUTE; private static Logger logger = LoggerFactory.getLogger(GcubeCkanDataCatalogServiceImpl.class); private final static String DEFAULT_ROLE = "OrganizationMember"; public final static String TEST_USER = "test.user"; public final static String TEST_SCOPE = "/gcube/devsec/devVRE"; public final static String TEST_MAIL = "test.user@test-com"; public final static String TEST_SEC_TOKEN = "4620e6d0-2313-4f48-9d54-eb3efd01a810"; public static final String CKAN_TOKEN_KEY = "ckanToken"; // ckan utils methods private CKanUtilsImpl instance; private Object LOCK = new Object(); /** * Since it needs the scope, we need to check if it is null or not * @return */ private CKanUtilsImpl getCkanUtilsObj(){ if(instance == null){ synchronized(LOCK){ if(instance == null){ // retrieve ckan information try{ String currentScope = ScopeProvider.instance.get(); logger.debug("Scope is " + currentScope); instance = new CKanUtilsImpl(currentScope); }catch(Exception e){ logger.error("Unable to retrieve ckan information", e); } } } } return instance; } /* (non-Javadoc) * @see org.gcube.portlets.gcubeckan.gcubeckandatacatalog.client.GcubeCkanDataCatalogService#getCKanConnector(java.lang.String, java.lang.String) */ @Override public CkanConnectorAccessPoint getCKanConnector(String pathInfoParameter, String queryStringParameters) throws Exception { logger.info("getCKanConnector [pathInfo: "+pathInfoParameter + ", query: "+queryStringParameters+"]"); try{ if(queryStringParameters!=null && Base64.isBase64(queryStringParameters.getBytes())){ byte[] valueDecoded=Base64.decodeBase64(queryStringParameters.getBytes()); queryStringParameters = new String(valueDecoded); logger.info("queryStringParameters detected like Base64 and decoded like: "+queryStringParameters); } CkanConnectorAccessPoint ckAP = getCkanConnectorAccessPoint(pathInfoParameter, queryStringParameters); SessionUtil.saveCkanAccessPoint(this.getThreadLocalRequest().getSession(), ckAP); logger.info("Builded URI to CKAN Connector: "+ckAP.buildURI()); logger.debug("returning ckanConnectorUri: "+ckAP); return ckAP; // return "http://ckan-d-d4s.d4science.org"; }catch(Exception e ){ String message = "Sorry an error occurred during contacting gCube Ckan Data Catalogue"; logger.error(message, e); throw new Exception(message); } } /** * Gets the ckan connector access point. * * @param pathInfoParameter the path info parameter * @param queryStringParameters the query string parameters * @return the ckan connector access point */ private CkanConnectorAccessPoint getCkanConnectorAccessPoint(String pathInfoParameter, String queryStringParameters) { if(outsidePortal()){ CkanConnectorAccessPoint ckan = new CkanConnectorAccessPoint(getCkanUtilsObj().getCatalogueUrl()); return ckan; } //CKAN BASE URL ASLSession session = getASLSession(this.getThreadLocalRequest().getSession()); GcoreEndpointReader ckanEndPoint = SessionUtil.getCkanEndPoint(this.getThreadLocalRequest().getSession(), session.getScope()); String ckanConnectorBaseUrl = ckanEndPoint.getCkanResourceEntyName(); ckanConnectorBaseUrl = ckanConnectorBaseUrl.startsWith(HTTP) && !ckanConnectorBaseUrl.startsWith(HTTPS)?ckanConnectorBaseUrl.replaceFirst(HTTP, HTTPS):ckanConnectorBaseUrl; ckanConnectorBaseUrl = ckanConnectorBaseUrl.contains(PORT_HTTP)?ckanConnectorBaseUrl.replace(PORT_HTTP, PORT_HTTPS):ckanConnectorBaseUrl; logger.debug("Base URL is: "+ckanConnectorBaseUrl); //GET CONTEXT String ckanContext = getServletContext().getInitParameter(CKANCONNECTORCONTEXT); logger.debug(CKANCONNECTORCONTEXT + " is: "+ckanContext); ckanContext= ckanContext!=null?ckanContext:""; CkanConnectorAccessPoint ckan = new CkanConnectorAccessPoint(ckanConnectorBaseUrl+ckanContext); pathInfoParameter = CkanConnectorAccessPoint.checkURLPathSeparator(pathInfoParameter, true, false); logger.debug("External Path Info parameter: "+pathInfoParameter); //ADD PATH INFO ckan.addPathInfo(pathInfoParameter); logger.debug("CKanConnector pathInfo: "+ckan.getPathInfoParameter()); ckan.addQueryString(queryStringParameters); //GET TOKEN String gcubeTokenValue = null; if(SessionUtil.isIntoPortal()){ gcubeTokenValue = getGcubeSecurityToken(); }else{ logger.warn("******** Using TEST_USER security token!!!"); gcubeTokenValue = TEST_SEC_TOKEN; } ckan.addGubeToken(gcubeTokenValue); //ADDING LIST OF VRE TO WHICH USER BELONGS if(!SessionUtil.isIntoPortal()){ return ckan; } List listVres = UserUtil.getListVreForUser(session.getUserEmailAddress()); ckan.addListOfVREs(listVres); return ckan; } /** * Gets the gcube security token. * * @return the gcube security token */ protected String getGcubeSecurityToken() { HttpSession httpSession = this.getThreadLocalRequest().getSession(); ASLSession session = getASLSession(httpSession); logger.debug("Get security token return: "+session.getSecurityToken()); if(session.getSecurityToken()==null || session.getSecurityToken().isEmpty()){ logger.warn("Security token retured from ASL is null or empty, I'm setting security token..."); setAuthorizationToken(session); } return session.getSecurityToken(); } /** * Temporary method to set the authorization token. * * @param session the new authorization token */ private static void setAuthorizationToken(ASLSession session) { String username = session.getUsername(); String scope = session.getScope(); ScopeProvider.instance.set(scope); logger.debug("calling service token on scope " + scope); List userRoles = new ArrayList(); userRoles.add(DEFAULT_ROLE); session.setSecurityToken(null); String token = authorizationService().build().generate(session.getUsername(), userRoles); logger.debug("received token: "+token); session.setSecurityToken(token); logger.info("Security token set in session for: "+username + " on " + scope); } /** * Gets the ASL session. * * @param httpSession the http session * @return the ASL session */ protected ASLSession getASLSession(HttpSession httpSession) { String sessionID = httpSession.getId(); String user = (String) httpSession.getAttribute(USERNAME_ATTRIBUTE); if (user == null) { logger.warn("****** STARTING IN TEST MODE - NO USER FOUND *******"); //for test only user = TEST_USER; httpSession.setAttribute(USERNAME_ATTRIBUTE, user); ASLSession session = SessionManager.getInstance().getASLSession(sessionID, user); //session.setScope(TEST_SCOPE); //session.setUserEmailAddress(TEST_MAIL); return session; } else logger.trace("user found in session "+user); return SessionManager.getInstance().getASLSession(sessionID, user); } /* (non-Javadoc) * @see org.gcube.portlets.gcubeckan.gcubeckandatacatalog.client.GcubeCkanDataCatalogService#getMyRole() */ @Override public CkanRole getMyRole() throws Exception{ HttpSession httpSession = this.getThreadLocalRequest().getSession(); // we need to evaluate which roles the user has in this scope String username = getASLSession(httpSession).getUsername(); String currentScope = getASLSession(httpSession).getScope(); String groupName = getASLSession(httpSession).getGroupName(); try{ if(!SessionUtil.isIntoPortal()){ logger.warn("OUT FROM PORTAL DETECTED RETURNING ROLE: "+CkanRole.ADMIN); return CkanRole.ADMIN; } // first of all, check if the user is a sysadmin in the catalog (in this case he can do everything) boolean isSysAdmin = getCkanUtilsObj().isSysAdmin(username, getUserCKanTokenFromSession()); if(isSysAdmin){ logger.debug("The user is a sysadmin of the catalog -> he can edit/add"); return CkanRole.SYSADMIN; }else{ // retrieve the liferay's roles for the user UserManager userManager = new LiferayUserManager(); RoleManager roleManager = new LiferayRoleManager(); GroupManager groupManager = new LiferayGroupManager(); List roles = roleManager.listRolesByUserAndGroup(userManager.getUserId(username), groupManager.getGroupId(groupName)); logger.debug("The list of roles for " + username + " into " + groupName + " is " + roles); // the default one String mainRole = "Catalogue-Member"; RolesIntoOrganization correspondentRoleToCheck = RolesIntoOrganization.MEMBER; // NOTE: it is supposed that there is just one role for this person correspondent to the one in the catalog for (GCubeRole role : roles) { logger.debug("User " + username + " has role " + role.getRoleName() + " in " + currentScope); if(role.getRoleName().equalsIgnoreCase(GatewayRolesNames.CATALOGUE_ADMIN.getRoleName())){ mainRole = GatewayRolesNames.CATALOGUE_ADMIN.getRoleName(); correspondentRoleToCheck = RolesIntoOrganization.ADMIN; break; }else if(role.getRoleName().equalsIgnoreCase(GatewayRolesNames.CATALOGUE_EDITOR.getRoleName())){ mainRole = GatewayRolesNames.CATALOGUE_EDITOR.getRoleName(); correspondentRoleToCheck = RolesIntoOrganization.EDITOR; break; } } // with this invocation, we check if the role is present in ckan and if it is not it will be added boolean res = getCkanUtilsObj().checkRole(username, groupName, correspondentRoleToCheck); if(res) return reMapRole(mainRole); } }catch(Exception e){ logger.error("Unable to retrieve the role information for this user. Returning member role", e); } logger.debug("Unable to check the role into ckan organization, returning MEMBER as role"); // return the base role return CkanRole.MEMBER; } /** * Map between roles. * * @param mainRole the main role * @return the ckan role */ private CkanRole reMapRole(String mainRole) { switch(mainRole){ case "Catalogue-Admin": return CkanRole.ADMIN; case "Catalogue-Editor": return CkanRole.EDITOR; case "Catalogue-Member": return CkanRole.MEMBER; default : return CkanRole.MEMBER; } } /* (non-Javadoc) * @see org.gcube.portlets.gcubeckan.gcubeckandatacatalog.client.GcubeCkanDataCatalogService#getUser() */ @Override public String getUser() { HttpSession httpSession = this.getThreadLocalRequest().getSession(); logger.debug("User in session is " + getASLSession(httpSession).getUsername()); return getASLSession(httpSession).getUsername(); } /** * Get current user's token. * * @return String the ckan user's token */ private String getUserCKanTokenFromSession(){ HttpSession httpSession = this.getThreadLocalRequest().getSession(); ASLSession session = getASLSession(httpSession); String username = session.getUsername(); logger.debug("User in session is " + username); try{ String token = null; if(this.getThreadLocalRequest().getSession().getAttribute(CKAN_TOKEN_KEY) != null) token = (String)this.getThreadLocalRequest().getSession().getAttribute(CKAN_TOKEN_KEY); else{ token = getCkanUtilsObj().getApiKeyFromUsername(username); this.getThreadLocalRequest().getSession().setAttribute(CKAN_TOKEN_KEY, token); logger.debug("Ckan token has been set for user " + username); } logger.debug("Found ckan token " + token + " for user " + username); return token; }catch(Exception e){ logger.error("Error while retrieving the key" , e); } return null; } @Override public String logoutURIFromCkan() { HttpSession httpSession = this.getThreadLocalRequest().getSession(); ASLSession session = getASLSession(httpSession); String username = session.getUsername(); CkanConnectorAccessPoint ckanAP = SessionUtil.getCkanAccessPoint(this.getThreadLocalRequest().getSession()); // String token = getGcubeSecurityToken(); logger.info("Logout from CKAN for: "+username +" by token: "+ckanAP.getGcubeTokenValue()); String ckanConnectorLogut = getServletContext().getInitParameter(CKANCONNECTORLOGOUT); logger.debug(CKANCONNECTORLOGOUT + " is: "+ckanConnectorLogut); CkanConnectorAccessPoint ckan = new CkanConnectorAccessPoint(ckanAP.getBaseUrl()); ckan.addPathInfo(ckanConnectorLogut); ckan.addGubeToken(ckanAP.getGcubeTokenValue()); return ckan.buildURI(); } /* (non-Javadoc) * @see org.gcube.portlets.gcubeckan.gcubeckandatacatalog.client.GcubeCkanDataCatalogService#logoutFromCkan() */ @Override public void logoutFromCkan() { HttpSession httpSession = this.getThreadLocalRequest().getSession(); ASLSession session = getASLSession(httpSession); String username = session.getUsername(); CkanConnectorAccessPoint ckanAP = SessionUtil.getCkanAccessPoint(this.getThreadLocalRequest().getSession()); // String token = getGcubeSecurityToken(); logger.info("Logout from CKAN for: "+username +" by token: "+ckanAP.getGcubeTokenValue()); String ckanConnectorLogut = getServletContext().getInitParameter(CKANCONNECTORLOGOUT); logger.debug(CKANCONNECTORLOGOUT + " is: "+ckanConnectorLogut); CkanConnectorAccessPoint ckan = new CkanConnectorAccessPoint(ckanAP.getBaseUrl()); ckan.addPathInfo(ckanConnectorLogut); ckan.addGubeToken(ckanAP.getGcubeTokenValue()); URL url; try { String deleteURI = ckan.buildURI(); logger.debug("Perfoming HTTP delete to URI: "+deleteURI); url = new URL(deleteURI); HttpURLConnection httpCon = (HttpURLConnection) url.openConnection(); httpCon.setDoOutput(true); httpCon.setRequestProperty("Content-Type", "application/x-www-form-urlencoded" ); httpCon.setRequestMethod("DELETE"); httpCon.connect(); HttpServletResponse response = this.getThreadLocalResponse(); if(httpCon.getResponseCode()==HttpStatus.SC_OK){ response.setContentLength(httpCon.getContentLength()); Map> map = httpCon.getHeaderFields(); for (String key : map.keySet()) { String hf = httpCon.getHeaderField(key); logger.trace("key: "+key +", value: "+hf); if(key==null){ logger.trace("skyp key: "+key +", value: "+hf); }else response.setHeader(key,hf); } response.setContentLength(httpCon.getContentLength()); String encoding = httpCon.getContentEncoding(); encoding = encoding == null ? UTF_8 : encoding; response.setCharacterEncoding(encoding); response.setStatus(HttpStatus.SC_OK); InputStream in = httpCon.getInputStream(); ServletOutputStream out = response.getOutputStream(); IOUtils.copy(in, out); logger.info("Logout Completed, response code: "+HttpStatus.SC_OK); }else{ logger.warn("An error occurred during perfoming CKAN logout, Response status is: "+httpCon.getResponseCode()); } } catch (IOException e) { logger.error("An error occured during performing Logout from CKAN for: "+username +" by token: "+ckanAP.getGcubeTokenValue(), e); } } @Override public Map getCkanOrganizationsNamesAndUrlsForUser() { HttpSession httpSession = this.getThreadLocalRequest().getSession(); ASLSession session = getASLSession(httpSession); String username = session.getUsername(); Map toReturn = new HashMap<>(); if(!SessionUtil.isIntoPortal()){ logger.warn("You are not into the portal"); // toReturn.put("testVRE", "random"); // toReturn.put("testVRE2", "random"); // toReturn.put("testVRE3", "random"); // toReturn.put("testVRE4", "random"); // toReturn.put("testVRE5", "random"); } if(username.equals(TEST_USER)) return toReturn; else{ List organizations = instance.getOrganizationsByUser(username); for (CkanOrganization ckanOrganization : organizations) { toReturn.put(ckanOrganization.getTitle(), "/organization/" + ckanOrganization.getName()); } logger.debug("List of organizations to return for user " + username + " is " + toReturn); } return toReturn; } @Override public boolean outsidePortal() { HttpSession httpSession = this.getThreadLocalRequest().getSession(); ASLSession session = getASLSession(httpSession); String username = session.getUsername(); if(username.equals(TEST_USER)) return true; return false; } /* public static void main(String[] args) { URL url; try { String deleteURI = "https://ckan-d-d4s.d4science.org:443/ckan-connector/gcube/service/disconnect?gcube-token=4620e6d0-2313-4f48-9d54-eb3efd01a810"; logger.debug("Perfoming HTTP delete to URI: "+deleteURI); url = new URL(deleteURI); HttpURLConnection httpCon = (HttpURLConnection) url.openConnection(); httpCon.setDoOutput(true); httpCon.setRequestProperty("Content-Type", "application/x-www-form-urlencoded" ); httpCon.setRequestMethod("DELETE"); httpCon.connect(); if(httpCon.getResponseCode()==HttpStatus.SC_OK){ Map> map = httpCon.getHeaderFields(); for (String key : map.keySet()) { logger.trace("key: "+key +", value: "+map.get(key)); } InputStream in = httpCon.getInputStream(); String encoding = httpCon.getContentEncoding(); encoding = encoding == null ? "UTF-8" : encoding; String body = IOUtils.toString(in, encoding); logger.debug("response: "+body); logger.info("Logout Completed, response code: "+HttpStatus.SC_OK); }else{ logger.warn("An error occurred during perfoming CKAN logout, Response status is: "+httpCon.getResponseCode()); } } catch (IOException e) { logger.error("An error occured during performing Logout from CKAN", e); } }*/ }