package org.gcube.informationsystem.resourceregistry.contexts; import java.util.HashMap; import java.util.Map; import java.util.NoSuchElementException; import java.util.UUID; import org.gcube.common.security.ContextBean; import org.gcube.common.security.providers.SecretManagerProvider; import org.gcube.informationsystem.base.reference.IdentifiableElement; import org.gcube.informationsystem.contexts.reference.entities.Context; import org.gcube.informationsystem.contexts.reference.relations.IsParentOf; import org.gcube.informationsystem.model.reference.properties.Header; import org.gcube.informationsystem.resourceregistry.api.contexts.ContextCache; import org.gcube.informationsystem.resourceregistry.api.exceptions.ResourceRegistryException; import org.gcube.informationsystem.resourceregistry.api.exceptions.contexts.ContextException; import org.gcube.informationsystem.resourceregistry.api.exceptions.contexts.ContextNotFoundException; import org.gcube.informationsystem.resourceregistry.contexts.security.AdminSecurityContext; import org.gcube.informationsystem.resourceregistry.contexts.security.SecurityContext; import org.gcube.informationsystem.resourceregistry.contexts.security.SecurityContext.PermissionMode; import org.gcube.informationsystem.resourceregistry.instances.base.ElementManagementUtility; import org.gcube.informationsystem.resourceregistry.utils.Utility; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.orientechnologies.orient.core.db.ODatabaseRecordThreadLocal; import com.orientechnologies.orient.core.db.document.ODatabaseDocument; import com.orientechnologies.orient.core.record.ODirection; import com.orientechnologies.orient.core.record.OVertex; import com.orientechnologies.orient.core.record.impl.ODocument; import com.orientechnologies.orient.core.sql.executor.OResult; import com.orientechnologies.orient.core.sql.executor.OResultSet; /** * @author Luca Frosini (ISTI - CNR) */ public class ContextUtility { private static final Logger logger = LoggerFactory.getLogger(ContextUtility.class); private Map contexts; private static ContextUtility contextUtility; public static ContextUtility getInstance() { if(contextUtility == null) { contextUtility = new ContextUtility(); } return contextUtility; } private ContextUtility() { contexts = new HashMap<>(); } private static final InheritableThreadLocal hierarchicalMode = new InheritableThreadLocal() { @Override protected Boolean initialValue() { return false; } }; public static InheritableThreadLocal getHierarchicalMode() { return hierarchicalMode; } private static final InheritableThreadLocal includeInstanceContexts = new InheritableThreadLocal() { @Override protected Boolean initialValue() { return false; } }; public static InheritableThreadLocal getIncludeInstanceContexts() { return includeInstanceContexts; } public static String getCurrentContextFullName() { return SecretManagerProvider.instance.get().getContext(); } public static SecurityContext getCurrentSecurityContext() throws ResourceRegistryException { String fullName = getCurrentContextFullName(); if(fullName == null) { throw new ContextException("Null Token and Scope. Please set your token first."); } return ContextUtility.getInstance().getSecurityContextByFullName(fullName); } public static AdminSecurityContext getAdminSecurityContext() throws ResourceRegistryException { AdminSecurityContext adminSecurityContext = AdminSecurityContext.getInstance(); return adminSecurityContext; } public synchronized void addSecurityContext(SecurityContext securityContext) { contexts.put(securityContext.getUUID(), securityContext); } public synchronized void addSecurityContext(String fullname, SecurityContext securityContext) { contexts.put(securityContext.getUUID(), securityContext); } public synchronized SecurityContext getSecurityContextByFullName(String fullName) throws ContextException { ODatabaseDocument current = ContextUtility.getCurrentODatabaseDocumentFromThreadLocal(); ODatabaseDocument oDatabaseDocument = null; try { SecurityContext securityContext = null; logger.trace("Trying to get {} for {}", SecurityContext.class.getSimpleName(), fullName); UUID uuid = ContextCache.getInstance().getUUIDByFullName(fullName); if(uuid != null) { securityContext = contexts.get(uuid); } if(securityContext==null) { logger.trace("{} for {} is not in cache. Going to get it", SecurityContext.class.getSimpleName(), fullName); oDatabaseDocument = getAdminSecurityContext().getDatabaseDocument(PermissionMode.READER); OVertex contextVertex = getContextVertexByFullName(oDatabaseDocument, fullName); ODocument oDocument = contextVertex.getProperty(IdentifiableElement.HEADER_PROPERTY); uuid = UUID.fromString(oDocument.getProperty(Header.UUID_PROPERTY)); securityContext = getSecurityContextByUUID(uuid, contextVertex); addSecurityContext(fullName, securityContext); } return securityContext; } catch(ContextException e) { throw e; } catch(Exception e) { throw new ContextException("Unable to retrieve Context UUID from current Context", e); } finally { if(oDatabaseDocument!=null) { oDatabaseDocument.close(); } if(current!=null) { current.activateOnCurrentThread(); } } } public SecurityContext getSecurityContextByUUID(UUID uuid) throws ResourceRegistryException { return getSecurityContextByUUID(uuid, null); } public static ODatabaseDocument getCurrentODatabaseDocumentFromThreadLocal() { ODatabaseDocument current = null; try { current = (ODatabaseDocument) ODatabaseRecordThreadLocal.instance().get(); }catch (Exception e) { // It is possible that there is no current ODatabaseDocument } return current; } private OVertex getContextVertexByUUID(UUID uuid) throws ResourceRegistryException { ODatabaseDocument current = ContextUtility.getCurrentODatabaseDocumentFromThreadLocal(); ODatabaseDocument oDatabaseDocument = null; try { oDatabaseDocument = getAdminSecurityContext().getDatabaseDocument(PermissionMode.READER); OVertex oVertex = Utility.getElementByUUID(oDatabaseDocument, Context.NAME, uuid, OVertex.class); return oVertex; } finally { if(oDatabaseDocument!=null) { oDatabaseDocument.close(); } if(current!=null) { current.activateOnCurrentThread(); } } } private SecurityContext getSecurityContextByUUID(UUID uuid, OVertex contextVertex) throws ResourceRegistryException { SecurityContext securityContext = contexts.get(uuid); if(securityContext == null) { securityContext = new SecurityContext(uuid); try { if(contextVertex == null) { contextVertex = getContextVertexByUUID(uuid); } OVertex parentVertex = contextVertex.getVertices(ODirection.IN, IsParentOf.NAME).iterator().next(); if(parentVertex != null) { UUID parentUUID = Utility.getUUID(parentVertex); securityContext.setParentSecurityContext(getSecurityContextByUUID(parentUUID, parentVertex)); } } catch(NoSuchElementException e) { // No parent } contexts.put(uuid, securityContext); } return securityContext; } /* protected UUID getContextUUIDFromFullName(String fullName) throws ResourceRegistryException { OVertex contextVertex = getContextVertexByFullName(fullName); return Utility.getUUID(contextVertex); } */ private OVertex getContextVertexByFullName(ODatabaseDocument oDatabaseDocument, String fullName) throws ResourceRegistryException { logger.trace("Going to get {} {} with full name '{}'", Context.NAME, OVertex.class.getSimpleName(), fullName); ContextBean scopeBean = new ContextBean(fullName); String name = scopeBean.name(); // TODO Rewrite better query. This query works because all the scope parts has a different name String select = "SELECT FROM " + Context.class.getSimpleName() + " WHERE " + Context.NAME_PROPERTY + " = :name"; Map map = new HashMap<>(); map.put("name", name); OResultSet resultSet = oDatabaseDocument.query(select, map); if(resultSet == null || !resultSet.hasNext()) { throw new ContextNotFoundException("Error retrieving context with name " + fullName); } OResult oResult = resultSet.next(); OVertex context = ElementManagementUtility.getElementFromOptional(oResult.getVertex()); logger.trace("Context Representing Vertex : {}", Utility.toJsonString(context, true)); if(resultSet.hasNext()) { throw new ContextNotFoundException("Found more than one context with name " + name + "but required the one with path" + fullName + ". Please Reimplement the query"); } return context; } }