From 0f645fdde7044aa012844ae119253c6868dc1700 Mon Sep 17 00:00:00 2001 From: lucio Date: Wed, 22 Jan 2020 16:41:12 +0100 Subject: [PATCH] commit for release --- distro/gcube-app.xml | 2 +- pom.xml | 7 +- .../storagehub/AuthorizationChecker.java | 14 +- .../storagehub/MyApplicationListener.java | 40 +++ .../data/access/storagehub/StorageHub.java | 1 + .../gcube/data/access/storagehub/Utils.java | 101 +++++-- .../accounting/AccountingHandler.java | 35 ++- .../handlers/Item2NodeConverter.java | 6 +- .../handlers/Node2ItemConverter.java | 4 +- .../storagehub/handlers/UnshareHandler.java | 25 +- .../handlers/VREQueryRetriever.java | 67 ++++- .../storagehub/services/ACLManager.java | 80 ++--- .../storagehub/services/GroupManager.java | 284 +++++++++++++++--- .../storagehub/services/ItemsCreator.java | 4 +- .../storagehub/services/ItemsManager.java | 30 +- .../storagehub/services/UserManager.java | 71 +++-- .../storagehub/services/WorkspaceManager.java | 8 +- src/main/webapp/WEB-INF/README | 2 +- src/main/webapp/WEB-INF/gcube-app.xml | 2 +- 19 files changed, 603 insertions(+), 180 deletions(-) create mode 100644 src/main/java/org/gcube/data/access/storagehub/MyApplicationListener.java diff --git a/distro/gcube-app.xml b/distro/gcube-app.xml index 4f0d8e1..aa6e541 100644 --- a/distro/gcube-app.xml +++ b/distro/gcube-app.xml @@ -1,7 +1,7 @@ StorageHub DataAccess - 1.0.0-SNAPSHOT + ${version} Storage Hub webapp \ No newline at end of file diff --git a/pom.xml b/pom.xml index 474122e..ab8da1a 100644 --- a/pom.xml +++ b/pom.xml @@ -12,7 +12,7 @@ 4.0.0 org.gcube.data.access storagehub - 1.0.8 + 1.0.9 storagehub @@ -97,11 +97,6 @@ common-scope - - org.gcube.core - common-encryption - - org.gcube.common storagehub-model diff --git a/src/main/java/org/gcube/data/access/storagehub/AuthorizationChecker.java b/src/main/java/org/gcube/data/access/storagehub/AuthorizationChecker.java index ad792be..a294964 100644 --- a/src/main/java/org/gcube/data/access/storagehub/AuthorizationChecker.java +++ b/src/main/java/org/gcube/data/access/storagehub/AuthorizationChecker.java @@ -44,7 +44,7 @@ public class AuthorizationChecker { Item item = node2Item.getItem(node, Excludes.ALL); - if (item==null) throw new UserNotAuthorizedException("Insufficent Provileges for user "+login+" to read node with id "+id+": it's not a valid StorageHub node"); + if (item==null) throw new UserNotAuthorizedException("Insufficent Privileges for user "+login+" to read node with id "+id+": it's not a valid StorageHub node"); @@ -64,10 +64,10 @@ public class AuthorizationChecker { if (!authorizable.isGroup() && entry.getPrincipal().getName().equals(login)) return; if (authorizable.isGroup() && ((Group) authorizable).isMember(userAuthorizable)) return; } - throw new UserNotAuthorizedException("Insufficent Provileges for user "+login+" to read node with id "+id); + throw new UserNotAuthorizedException("Insufficent Privileges for user "+login+" to read node with id "+id); } else if (item.getOwner()==null || !item.getOwner().equals(login)) - throw new UserNotAuthorizedException("Insufficent Provileges for user "+login+" to read node with id "+id); + throw new UserNotAuthorizedException("Insufficent Privileges for user "+login+" to read node with id "+id); } @@ -86,10 +86,10 @@ public class AuthorizationChecker { String login = AuthorizationProvider.instance.get().getClient().getId(); - if (item==null) throw new UserNotAuthorizedException("Insufficent Provileges for user "+login+" to write into node with id "+id+": it's not a valid StorageHub node"); + if (item==null) throw new UserNotAuthorizedException("Insufficent Privileges for user "+login+" to write into node with id "+id+": it's not a valid StorageHub node"); if (Constants.WRITE_PROTECTED_FOLDER.contains(item.getName()) || Constants.WRITE_PROTECTED_FOLDER.contains(item.getTitle())) - throw new UserNotAuthorizedException("Insufficent Provileges for user "+login+" to write into node with id "+id+": it's a protected folder"); + throw new UserNotAuthorizedException("Insufficent Privileges for user "+login+" to write into node with id "+id+": it's a protected folder"); if (item.isShared()) { Node parentSharedNode = retrieveSharedFolderParent(node, session); @@ -116,7 +116,7 @@ public class AuthorizationChecker { } else if(item.getOwner().equals(login)) return; - throw new UserNotAuthorizedException("Insufficent Provileges for user "+login+" to write into node with id "+id); + throw new UserNotAuthorizedException("Insufficent Privileges for user "+login+" to write into node with id "+id); } @@ -132,7 +132,7 @@ public class AuthorizationChecker { //TODO: riguardare questo pezzo di codice String login = AuthorizationProvider.instance.get().getClient().getId(); - if (item==null) throw new UserNotAuthorizedException("Insufficent Provileges for user "+login+": it's not a valid StorageHub node"); + if (item==null) throw new UserNotAuthorizedException("Insufficent Privileges for user "+login+": it's not a valid StorageHub node"); Node node = session.getNodeByIdentifier(item.getId()); diff --git a/src/main/java/org/gcube/data/access/storagehub/MyApplicationListener.java b/src/main/java/org/gcube/data/access/storagehub/MyApplicationListener.java new file mode 100644 index 0000000..0600c8d --- /dev/null +++ b/src/main/java/org/gcube/data/access/storagehub/MyApplicationListener.java @@ -0,0 +1,40 @@ +package org.gcube.data.access.storagehub; + +import javax.inject.Inject; + +import org.apache.jackrabbit.api.JackrabbitRepository; +import org.gcube.data.access.storagehub.services.GroupManager; +import org.gcube.data.access.storagehub.services.RepositoryInitializer; +import org.glassfish.jersey.server.monitoring.ApplicationEvent; +import org.glassfish.jersey.server.monitoring.ApplicationEventListener; +import org.glassfish.jersey.server.monitoring.RequestEvent; +import org.glassfish.jersey.server.monitoring.RequestEventListener; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class MyApplicationListener implements ApplicationEventListener { + + private static final Logger log = LoggerFactory.getLogger(MyApplicationListener.class); + + @Inject + RepositoryInitializer repository; + + @Override + public void onEvent(ApplicationEvent event) { + switch (event.getType()) { + case DESTROY_FINISHED: + log.info("Destroying application storageHub"); + ((JackrabbitRepository) repository.getRepository()).shutdown(); + log.info("Jackrabbit repository stopped"); + default: + break; + } + } + + @Override + public RequestEventListener onRequest(RequestEvent requestEvent) { + return null; + } + + +} \ No newline at end of file diff --git a/src/main/java/org/gcube/data/access/storagehub/StorageHub.java b/src/main/java/org/gcube/data/access/storagehub/StorageHub.java index da500a3..53ba0be 100644 --- a/src/main/java/org/gcube/data/access/storagehub/StorageHub.java +++ b/src/main/java/org/gcube/data/access/storagehub/StorageHub.java @@ -32,6 +32,7 @@ public class StorageHub extends Application { classes.add(UserManager.class); classes.add(GroupManager.class); classes.add(SerializableErrorEntityTextWriter.class); + classes.add(MyApplicationListener.class); return classes; } diff --git a/src/main/java/org/gcube/data/access/storagehub/Utils.java b/src/main/java/org/gcube/data/access/storagehub/Utils.java index 62f4ec8..ed7448a 100644 --- a/src/main/java/org/gcube/data/access/storagehub/Utils.java +++ b/src/main/java/org/gcube/data/access/storagehub/Utils.java @@ -22,11 +22,16 @@ import javax.jcr.RepositoryException; import javax.jcr.Session; import javax.jcr.lock.Lock; import javax.jcr.lock.LockException; +import javax.jcr.query.Query; import javax.jcr.version.Version; import org.apache.commons.io.FilenameUtils; +import org.apache.jackrabbit.util.ISO9075; import org.apache.jackrabbit.util.Text; import org.gcube.common.authorization.library.provider.AuthorizationProvider; +import org.gcube.common.scope.api.ScopeProvider; +import org.gcube.common.scope.impl.ScopeBean; +import org.gcube.common.scope.impl.ScopeBean.Type; import org.gcube.common.storagehub.model.Excludes; import org.gcube.common.storagehub.model.Paths; import org.gcube.common.storagehub.model.exceptions.BackendGenericError; @@ -39,15 +44,13 @@ import org.gcube.common.storagehub.model.items.Item; import org.gcube.common.storagehub.model.items.SharedFolder; import org.gcube.common.storagehub.model.types.ItemAction; import org.gcube.common.storagehub.model.types.NodeProperty; -import org.gcube.contentmanager.storageclient.wrapper.AccessType; -import org.gcube.contentmanager.storageclient.wrapper.MemoryType; -import org.gcube.contentmanager.storageclient.wrapper.StorageClient; import org.gcube.data.access.storagehub.accounting.AccountingHandler; import org.gcube.data.access.storagehub.handlers.Item2NodeConverter; import org.gcube.data.access.storagehub.handlers.Node2ItemConverter; import org.gcube.data.access.storagehub.handlers.StorageBackendHandler; +import org.gcube.data.access.storagehub.handlers.VRE; +import org.gcube.data.access.storagehub.handlers.VREManager; import org.gcube.data.access.storagehub.handlers.VersionHandler; -import org.gcube.data.access.storagehub.storage.backend.impl.GCubeStorageBackend; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -107,15 +110,30 @@ public class Utils { } + public static List serachByNameOnFolder(Session ses, Node parent, List excludes, Range range, boolean showHidden, Class nodeTypeToInclude, String nameParam) throws RepositoryException, BackendGenericError{ + String xpath = String.format("/jcr:root%s//element(*,nthl:workspaceItem)[jcr:like(@jcr:title, '%s')]",ISO9075.encodePath(parent.getPath()), nameParam); + + //String query = String.format("SELECT * FROM [nthl:workspaceLeafItem] AS node WHERE ISDESCENDANTNODE('%s') ORDER BY node.[jcr:lastModified] DESC ",vreFolder.getPath()); + logger.trace("query for search is {}",xpath); + + Query jcrQuery = ses.getWorkspace().getQueryManager().createQuery(xpath, Query.XPATH); + + NodeIterator it = jcrQuery.execute().getNodes(); + return getItemListFromNodeIterator(it, excludes, range, showHidden, nodeTypeToInclude); + } + public static List getItemList(Node parent, List excludes, Range range, boolean showHidden, Class nodeTypeToInclude) throws RepositoryException, BackendGenericError{ - - logger.debug("getting children of node {}", parent.getIdentifier()); - - List returnList = new ArrayList(); + logger.trace("getting children of node {}", parent.getIdentifier()); long start = System.currentTimeMillis(); NodeIterator iterator = parent.getNodes(); logger.trace("time to get iterator {}",(System.currentTimeMillis()-start)); + return getItemListFromNodeIterator(iterator, excludes, range, showHidden, nodeTypeToInclude); + } + + private static List getItemListFromNodeIterator(NodeIterator iterator, List excludes, Range range, boolean showHidden, Class nodeTypeToInclude) throws RepositoryException, BackendGenericError{ + List returnList = new ArrayList(); + logger.trace("nodeType is {}",nodeTypeToInclude); int count =0; logger.trace("selected range is {}", range); @@ -123,12 +141,12 @@ public class Utils { while (iterator.hasNext()){ Node current = iterator.nextNode(); - logger.debug("current node "+current.getName()); + logger.trace("current node "+current.getName()); if (isToExclude(current, showHidden)) continue; - logger.debug("current node not excluded "+current.getName()); + logger.trace("current node not excluded "+current.getName()); if (range==null || (count>=range.getStart() && returnList.size() tempQueue = new LinkedList(); - logger.debug("adding directory {}",currentNode.getPath()); + logger.trace("adding directory {}",currentNode.getPath()); for (Item item : Utils.getItemList(currentNode,Excludes.GET_ONLY_CONTENT, null, false, null)){ if (excludes.contains(item.getId())) continue; if (item instanceof FolderItem) tempQueue.addAll(getAllNodesForZip((FolderItem) item, session, accountingHandler, excludes)); else if (item instanceof AbstractFileItem){ - logger.debug("adding file {}",item.getPath()); + logger.trace("adding file {}",item.getPath()); AbstractFileItem fileItem = (AbstractFileItem) item; accountingHandler.createReadObj(fileItem.getTitle(), session, session.getNodeByIdentifier(item.getId()), false); queue.addLast(item); @@ -188,9 +206,9 @@ public class Utils { Item item = queue.pop(); if (item instanceof FolderItem) { actualPath = Paths.getPath(item.getPath()); - logger.debug("actualPath is {}",actualPath.toPath()); + logger.trace("actualPath is {}",actualPath.toPath()); String name = Paths.remove(actualPath, originalPath).toPath().replaceFirst("/", ""); - logger.debug("writing dir {}",name); + logger.trace("writing dir {}",name); if (name.isEmpty()) continue; try { zos.putNextEntry(new ZipEntry(name)); @@ -206,7 +224,7 @@ public class Utils { } try(BufferedInputStream is = new BufferedInputStream(streamToWrite)){ String name = (Paths.remove(actualPath, originalPath).toPath()+item.getName()).replaceFirst("/", ""); - logger.debug("writing file {}",name); + logger.trace("writing file {}",name); zos.putNextEntry(new ZipEntry(name)); copyStream(is, zos); }catch (Exception e) { @@ -283,7 +301,7 @@ public class Utils { String nameTocheck = ext.isEmpty()? String.format("%s(*)",filename): String.format("%s(*).%s",filename, ext); - logger.debug("filename is {}, extension is {} , and name to check is {}", filename, ext, nameTocheck); + logger.trace("filename is {}, extension is {} , and name to check is {}", filename, ext, nameTocheck); NodeIterator ni = destination.getNodes(nameTocheck); int maxval = 0; @@ -323,8 +341,10 @@ public class Utils { //item.setHidden(destinationItem.isHidden()); Node newNode = new Item2NodeConverter().getNode(destinationNode, item); - if (accountingHandler!=null) - accountingHandler.createFolderAddObj(name, item.getClass().getSimpleName(), null, ses, newNode, false); + if (accountingHandler!=null) { + accountingHandler.createFolderAddObj(name, item.getClass().getSimpleName(), null, ses, destinationNode, false); + accountingHandler.createEntryCreate(item.getTitle(), ses, newNode, false); + } return newNode; } @@ -349,8 +369,10 @@ public class Utils { //item.setHidden(destinationItem.isHidden()); Node newNode = new Item2NodeConverter().getNode(destinationNode, item); - if (accountingHandler!=null) - accountingHandler.createFolderAddObj(name, item.getClass().getSimpleName(), null, ses, newNode, false); + if (accountingHandler!=null) { + accountingHandler.createFolderAddObj(name, item.getClass().getSimpleName(), null, ses, destinationNode, false); + accountingHandler.createEntryCreate(item.getTitle(), ses, newNode, false); + } return newNode; } @@ -380,4 +402,43 @@ public class Utils { node.setProperty(NodeProperty.LAST_MODIFIED_BY.toString(), login); node.setProperty(NodeProperty.LAST_ACTION.toString(), action.name()); } + + public static synchronized VRE getVreFolderItem(Session ses, Node2ItemConverter node2Item, VREManager vreManager, List excludes ) throws RepositoryException, BackendGenericError{ + org.gcube.common.storagehub.model.Path vrePath = Paths.append(Utils.getWorkspacePath(), Constants.VRE_FOLDER_PARENT_NAME); + ScopeBean bean = new ScopeBean(ScopeProvider.instance.get()); + if (!bean.is(Type.VRE)) throw new BackendGenericError("the current scope is not a VRE"); + String entireScopeName= bean.toString().replaceAll("^/(.*)/?$", "$1").replaceAll("/", "-"); + VRE vre = vreManager.getVRE(entireScopeName); + if (vre!=null) return vre; + else { + String query = String.format("SELECT * FROM [nthl:workspaceItem] As node WHERE node.[jcr:title] like '%s' AND ISDESCENDANTNODE('%s')",entireScopeName, vrePath.toPath()); + Query jcrQuery = ses.getWorkspace().getQueryManager().createQuery(query, Constants.QUERY_LANGUAGE); + NodeIterator it = jcrQuery.execute().getNodes(); + + if (!it.hasNext()) throw new BackendGenericError("vre folder not found for context "+entireScopeName); + + Node folder = it.nextNode(); + Item vreFolder = node2Item.getItem(folder, excludes); + return vreManager.putVRE(vreFolder); + } + + } + + public static synchronized VRE getVreFolderItemByGroupNameAndUser(Session ses, String goupName, String userId, Node2ItemConverter node2Item, VREManager vreManager, List excludes ) throws RepositoryException, BackendGenericError{ + org.gcube.common.storagehub.model.Path vrePath = Paths.append(Utils.getWorkspacePath(userId), Constants.VRE_FOLDER_PARENT_NAME); + VRE vre = vreManager.getVRE(goupName); + if (vre!=null) return vre; + else { + String query = String.format("SELECT * FROM [nthl:workspaceItem] As node WHERE node.[jcr:title] like '%s' AND ISDESCENDANTNODE('%s')",goupName, vrePath.toPath()); + Query jcrQuery = ses.getWorkspace().getQueryManager().createQuery(query, Constants.QUERY_LANGUAGE); + NodeIterator it = jcrQuery.execute().getNodes(); + + if (!it.hasNext()) throw new BackendGenericError("vre folder not found for context "+goupName); + + Node folder = it.nextNode(); + Item vreFolder = node2Item.getItem(folder, excludes); + return vreManager.putVRE(vreFolder); + } + } + } diff --git a/src/main/java/org/gcube/data/access/storagehub/accounting/AccountingHandler.java b/src/main/java/org/gcube/data/access/storagehub/accounting/AccountingHandler.java index d2986c0..d321019 100644 --- a/src/main/java/org/gcube/data/access/storagehub/accounting/AccountingHandler.java +++ b/src/main/java/org/gcube/data/access/storagehub/accounting/AccountingHandler.java @@ -70,6 +70,25 @@ public class AccountingHandler { } } + public void createEntryCreate(String title, Session ses, Node node, boolean saveHistory ) { + try { + + if (!node.hasNode(NodeProperty.ACCOUNTING.toString())){ + node.addNode(NodeProperty.ACCOUNTING.toString(), NodeProperty.NT_ACCOUNTING.toString()); + } + + Node accountingNodeParent = node.getNode(NodeProperty.ACCOUNTING.toString()); + Node accountingNode = accountingNodeParent.addNode(UUID.randomUUID().toString(),AccountingEntryType.CREATE.getNodeTypeDefinition()); + accountingNode.setProperty(USER, AuthorizationProvider.instance.get().getClient().getId()); + accountingNode.setProperty(DATE, Calendar.getInstance()); + accountingNode.setProperty(ITEM_NAME, title); + + if (saveHistory) ses.save(); + } catch (RepositoryException e) { + logger.warn("error trying to retrieve accountign node",e); + } + } + public void createFileUpdated(String title, Session ses, Node node, boolean saveHistory ) { try { @@ -107,15 +126,15 @@ public class AccountingHandler { } - public void createFolderAddObj(String title, String itemType, String mimeType, Session ses, Node node, boolean saveHistory ) { + public void createFolderAddObj(String title, String itemType, String mimeType, Session ses, Node parentNode, boolean saveHistory ) { try { - Node directoryNode = node.getParent(); - if (!directoryNode.hasNode(NodeProperty.ACCOUNTING.toString())){ - directoryNode.addNode(NodeProperty.ACCOUNTING.toString(), NodeProperty.NT_ACCOUNTING.toString()); + + if (!parentNode.hasNode(NodeProperty.ACCOUNTING.toString())){ + parentNode.addNode(NodeProperty.ACCOUNTING.toString(), NodeProperty.NT_ACCOUNTING.toString()); } - Node accountingNodeParent = directoryNode.getNode(NodeProperty.ACCOUNTING.toString()); + Node accountingNodeParent = parentNode.getNode(NodeProperty.ACCOUNTING.toString()); Node accountingNode = accountingNodeParent.addNode(UUID.randomUUID().toString(),AccountingEntryType.ADD.getNodeTypeDefinition()); accountingNode.setProperty(USER, AuthorizationProvider.instance.get().getClient().getId()); accountingNode.setProperty(DATE, Calendar.getInstance()); @@ -172,7 +191,7 @@ public class AccountingHandler { } } - public void createUnshareFolder(String title, Session ses, Node sharedNode, boolean saveHistory ) { + public void createUnshareFolder(String title, String user, Session ses, Node sharedNode, boolean saveHistory ) { try { if (!sharedNode.hasNode(NodeProperty.ACCOUNTING.toString())){ @@ -180,8 +199,8 @@ public class AccountingHandler { } Node accountingNodeParent = sharedNode.getNode(NodeProperty.ACCOUNTING.toString()); - Node accountingNode = accountingNodeParent.addNode(UUID.randomUUID().toString(),AccountingEntryType.SHARE.getNodeTypeDefinition()); - accountingNode.setProperty(USER, AuthorizationProvider.instance.get().getClient().getId()); + Node accountingNode = accountingNodeParent.addNode(UUID.randomUUID().toString(),AccountingEntryType.UNSHARE.getNodeTypeDefinition()); + accountingNode.setProperty(USER, user); accountingNode.setProperty(DATE, Calendar.getInstance()); accountingNode.setProperty(ITEM_NAME, title); diff --git a/src/main/java/org/gcube/data/access/storagehub/handlers/Item2NodeConverter.java b/src/main/java/org/gcube/data/access/storagehub/handlers/Item2NodeConverter.java index 1b72bd0..68e4e09 100644 --- a/src/main/java/org/gcube/data/access/storagehub/handlers/Item2NodeConverter.java +++ b/src/main/java/org/gcube/data/access/storagehub/handlers/Item2NodeConverter.java @@ -56,7 +56,7 @@ public class Item2NodeConverter { field.setAccessible(true); try{ //Class returnType = field.getType(); - logger.debug("creating node - added field {}",field.getName()); + logger.trace("creating node - added field {}",field.getName()); Values values = getObjectValue(field.getType(), field.get(item)); if (values.isMulti()) newNode.setProperty(attribute.value(), values.getValues()); else newNode.setProperty(attribute.value(), values.getValue()); @@ -67,7 +67,7 @@ public class Item2NodeConverter { NodeAttribute nodeAttribute = field.getAnnotation(NodeAttribute.class); if (nodeAttribute.isReadOnly()) continue; String nodeName = nodeAttribute.value(); - logger.debug("retrieving field node "+field.getName()); + logger.trace("retrieving field node "+field.getName()); field.setAccessible(true); try{ Object obj = field.get(item); @@ -129,7 +129,7 @@ public class Item2NodeConverter { } } else if (field.isAnnotationPresent(ListNodes.class)){ - logger.debug("found field {} of type annotated as ListNodes in class {} on node {}", field.getName(), object.getClass().getName(), newNode.getName()); + logger.trace("found field {} of type annotated as ListNodes in class {} on node {}", field.getName(), object.getClass().getName(), newNode.getName()); field.setAccessible(true); List toSetList = (List) field.get(object); diff --git a/src/main/java/org/gcube/data/access/storagehub/handlers/Node2ItemConverter.java b/src/main/java/org/gcube/data/access/storagehub/handlers/Node2ItemConverter.java index f139d93..bddef6b 100644 --- a/src/main/java/org/gcube/data/access/storagehub/handlers/Node2ItemConverter.java +++ b/src/main/java/org/gcube/data/access/storagehub/handlers/Node2ItemConverter.java @@ -127,7 +127,7 @@ public class Node2ItemConverter { try{ Class returnType = field.getType(); field.set(item, getPropertyValue(returnType, node.getProperty(attribute.value()))); - logger.debug("retrieve item - added field {}",field.getName()); + logger.trace("retrieve item - added field {}",field.getName()); }catch(PathNotFoundException e){ logger.trace("the current node dosn't contain {} property",attribute.value()); } catch (Exception e ) { @@ -320,7 +320,7 @@ public class Node2ItemConverter { public boolean checkNodeType(Node node, Class classToCompare) throws BackendGenericError{ try { - logger.info("class from nodetype is {} and class to compare is {}",ClassHandler.instance().get(node.getPrimaryNodeType().getName()), classToCompare); + logger.trace("class from nodetype is {} and class to compare is {}",ClassHandler.instance().get(node.getPrimaryNodeType().getName()), classToCompare); return classToCompare.isAssignableFrom(ClassHandler.instance().get(node.getPrimaryNodeType().getName())); diff --git a/src/main/java/org/gcube/data/access/storagehub/handlers/UnshareHandler.java b/src/main/java/org/gcube/data/access/storagehub/handlers/UnshareHandler.java index 1387eb4..35646c6 100644 --- a/src/main/java/org/gcube/data/access/storagehub/handlers/UnshareHandler.java +++ b/src/main/java/org/gcube/data/access/storagehub/handlers/UnshareHandler.java @@ -27,6 +27,7 @@ import org.gcube.common.storagehub.model.items.FolderItem; import org.gcube.common.storagehub.model.items.Item; import org.gcube.common.storagehub.model.items.SharedFolder; import org.gcube.common.storagehub.model.types.ItemAction; +import org.gcube.common.storagehub.model.types.NodeProperty; import org.gcube.data.access.storagehub.AuthorizationChecker; import org.gcube.data.access.storagehub.Utils; import org.gcube.data.access.storagehub.accounting.AccountingHandler; @@ -115,6 +116,8 @@ public class UnshareHandler { unsharedNode = createUnsharedFolder(ses, parentNode, directoryName, item.getDescription(), login); + + List itemsToCopy = Utils.getItemList(sharedItemNode, Excludes.ALL, null, true, null); for (Item itemCopy: itemsToCopy) { @@ -122,7 +125,16 @@ public class UnshareHandler { log.debug("copying {} to {}", itemToCopyNode.getPath(), unsharedNode.getPath()); ses.move(itemToCopyNode.getPath(), String.format("%s/%s",unsharedNode.getPath(), itemToCopyNode.getName())); } + + unsharedNode.getNode(NodeProperty.ACCOUNTING.toString()).remove(); + ses.move(sharedItemNode.getNode(NodeProperty.ACCOUNTING.toString()).getPath(), String.format("%s/%s",unsharedNode.getPath(), NodeProperty.ACCOUNTING.toString())); + + accountingHandler.createUnshareFolder(sharedItemNode.getProperty(NodeProperty.TITLE.toString()).getString(), "ALL", ses, unsharedNode, false); + ses.save(); + }catch(Throwable t) { + log.error("erro unsharing all",t); + throw t; }finally { ses.getWorkspace().getLockManager().unlock(sharedItemNode.getPath()); } @@ -164,6 +176,9 @@ public class UnshareHandler { log.debug("removed Access control entry for user {}",login); Node sharedItemNode = ses.getNodeByIdentifier(item.getId()); + + accountingHandler.createUnshareFolder(sharedItemNode.getProperty(NodeProperty.TITLE.toString()).getString(), login, ses, sharedItemNode, false); + Node usersNode = sharedItemNode.getNode(NodeConstants.USERS_NAME); usersNode.remove(); Node newUsersNode = sharedItemNode.addNode(NodeConstants.USERS_NAME); @@ -175,7 +190,8 @@ public class UnshareHandler { }}); acm.setPolicy(sharedFolderNode.getPath(), acls); - + + ses.save(); return parentId; @@ -225,7 +241,11 @@ public class UnshareHandler { }}); acm.setPolicy(sharedFolderNode.getPath(), acls); - + + for (String user: usersToUnshare) { + accountingHandler.createUnshareFolder(sharedItemNode.getProperty(NodeProperty.TITLE.toString()).getString(), user, ses, sharedItemNode, false); + } + ses.save(); return item.getId(); @@ -242,7 +262,6 @@ public class UnshareHandler { Node parentNode = ses.getNodeByIdentifier(parentDirectoryId); Node userNode = ses.getNode(String.format("%s/%s",parentNode.getPath(), directoryName)); userNode.removeShare(); - accountingHandler.createUnshareFolder(directoryName, ses, parentNode, false); log.debug("directory removed for user {}",user); return parentDirectoryId; } diff --git a/src/main/java/org/gcube/data/access/storagehub/handlers/VREQueryRetriever.java b/src/main/java/org/gcube/data/access/storagehub/handlers/VREQueryRetriever.java index 955f486..fa4b726 100644 --- a/src/main/java/org/gcube/data/access/storagehub/handlers/VREQueryRetriever.java +++ b/src/main/java/org/gcube/data/access/storagehub/handlers/VREQueryRetriever.java @@ -1,9 +1,13 @@ package org.gcube.data.access.storagehub.handlers; +import java.text.SimpleDateFormat; +import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.Arrays; +import java.util.Calendar; import java.util.Iterator; import java.util.List; +import java.util.Locale; import java.util.concurrent.Callable; import javax.jcr.Credentials; @@ -17,10 +21,11 @@ import javax.jcr.observation.Event; import javax.jcr.observation.EventJournal; import javax.jcr.query.Query; +import org.apache.jackrabbit.util.ISO9075; import org.gcube.common.storagehub.model.Excludes; import org.gcube.common.storagehub.model.NodeConstants; import org.gcube.common.storagehub.model.items.Item; -import org.gcube.data.access.storagehub.Constants; +import org.gcube.common.storagehub.model.types.NodeProperty; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -29,7 +34,7 @@ public class VREQueryRetriever implements Callable> { private static final Logger logger = LoggerFactory.getLogger(VREQueryRetriever.class); private static final int CACHE_DIMENSION = 50; - + private Repository repository; private Credentials credentials; private Item vreFolder; @@ -37,7 +42,7 @@ public class VREQueryRetriever implements Callable> { long lastTimestamp =0; private Node2ItemConverter node2Item = new Node2ItemConverter(); - + public VREQueryRetriever(Repository repository, Credentials credentials, Item vreFolder) { super(); @@ -53,20 +58,50 @@ public class VREQueryRetriever implements Callable> { try { long start = System.currentTimeMillis(); ses = repository.login(credentials); - String query = String.format("SELECT * FROM [nthl:workspaceLeafItem] AS node WHERE ISDESCENDANTNODE('%s') ORDER BY node.[jcr:lastModified] DESC ",vreFolder.getPath()); - logger.trace("query for recents is {}",query); - Query jcrQuery = ses.getWorkspace().getQueryManager().createQuery(query, Constants.QUERY_LANGUAGE); + + Calendar now = Calendar.getInstance(); + now.add(Calendar.YEAR, -1); + SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS", Locale.ENGLISH); + String formattedDate = formatter.format(now.getTime()); + + + String xpath = String.format("/jcr:root%s//element(*,nthl:workspaceLeafItem)[@jcr:lastModified>xs:dateTime('%s')] order by @jcr:lastModified descending",ISO9075.encodePath(vreFolder.getPath()),formattedDate); + + //String query = String.format("SELECT * FROM [nthl:workspaceLeafItem] AS node WHERE ISDESCENDANTNODE('%s') ORDER BY node.[jcr:lastModified] DESC ",vreFolder.getPath()); + logger.debug("query for recents is {}",xpath); + + Query jcrQuery = ses.getWorkspace().getQueryManager().createQuery(xpath, Query.XPATH); + jcrQuery.setLimit(CACHE_DIMENSION); lastTimestamp = System.currentTimeMillis(); NodeIterator it = jcrQuery.execute().getNodes(); - logger.trace("query for recents took {}",System.currentTimeMillis()-start); + logger.debug("query for recents took {}",System.currentTimeMillis()-start); while (it.hasNext()) { Node node = it.nextNode(); - Item item =node2Item.getItem(node, Excludes.EXCLUDE_ACCOUNTING); + + //long lastModifiedTimeItem = node.getProperty(NodeProperty.LAST_MODIFIED.toString()).getLong(); + + //logger.debug("RECENTS - node {} has timestamp {} ", node.getProperty(NodeProperty.TITLE.toString()).getString(), lastModifiedTimeItem); + /* + if(cachedList.size()<=CACHE_DIMENSION || lastModifiedTimeItem>cachedList.get(CACHE_DIMENSION-1).getLastModificationTime().getTimeInMillis()) { + logger.debug("cachedList contains {}",cachedList.size()); + if(cachedList.size()>=CACHE_DIMENSION) cachedList.remove(CACHE_DIMENSION-1); + Item item =node2Item.getItem(node, Excludes.EXCLUDE_ACCOUNTING); + int insertposition =0; + for(Item cachedItem: cachedList) { + if (cachedItem.getLastModificationTime().before(item.getLastModificationTime())) + break; + insertposition++; + } + cachedList.add(insertposition, item); + logger.debug("RECENTS - adding item {} with node {} in position {}",item.getTitle(), node.getName(), insertposition); + }*/ + Item item = node2Item.getItem(node, Excludes.EXCLUDE_ACCOUNTING); + logger.debug("RECENTS - adding item {} with timestamp {}",item.getTitle(), item.getLastModificationTime().getTimeInMillis()); cachedList.add(item); - logger.trace("adding item {} with node {}",item.getTitle(), node.getName()); + } - logger.trace("creating objects took {}",System.currentTimeMillis()-start); + logger.debug("creating objects took {}",System.currentTimeMillis()-start); if (cachedList.size()<=10) return cachedList; else return cachedList.subList(0, 10); } catch (Exception e) { @@ -79,7 +114,7 @@ public class VREQueryRetriever implements Callable> { } } else { try { - + long timestampToUse = lastTimestamp; lastTimestamp = System.currentTimeMillis(); @@ -89,16 +124,16 @@ public class VREQueryRetriever implements Callable> { EventJournal journalChanged = ses.getWorkspace().getObservationManager().getEventJournal(Event.PROPERTY_CHANGED^Event.NODE_REMOVED^Event.NODE_MOVED^Event.NODE_ADDED, vreFolder.getPath(), true, null, types); journalChanged.skipTo(timestampToUse); - + logger.trace("getting the journal took {}",System.currentTimeMillis()-start); - + int events = 0; - + while (journalChanged.hasNext()) { events++; Event event = journalChanged.nextEvent(); switch(event.getType()) { - + case Event.NODE_ADDED: if (ses.nodeExists(event.getPath())) { Node nodeAdded = ses.getNode(event.getPath()); @@ -109,7 +144,7 @@ public class VREQueryRetriever implements Callable> { } } break; - + case Event.PROPERTY_CHANGED: if (ses.propertyExists(event.getPath())) { Property property = ses.getProperty(event.getPath()); diff --git a/src/main/java/org/gcube/data/access/storagehub/services/ACLManager.java b/src/main/java/org/gcube/data/access/storagehub/services/ACLManager.java index 9227378..3436006 100644 --- a/src/main/java/org/gcube/data/access/storagehub/services/ACLManager.java +++ b/src/main/java/org/gcube/data/access/storagehub/services/ACLManager.java @@ -2,7 +2,6 @@ package org.gcube.data.access.storagehub.services; import java.security.Principal; import java.util.ArrayList; -import java.util.Iterator; import java.util.List; import javax.enterprise.context.RequestScoped; @@ -22,7 +21,6 @@ import javax.ws.rs.PUT; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; -import javax.ws.rs.QueryParam; import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; @@ -143,45 +141,25 @@ public class ACLManager { InnerMethodName.instance.set("setACLById"); Session ses = null; try{ - + ses = repository.getRepository().login(CredentialHandler.getAdminCredentials(context)); - + Node node = ses.getNodeByIdentifier(id); Item item = node2Item.getItem(node, Excludes.ALL); - + if (!(item instanceof SharedFolder)) throw new InvalidItemException("the item is not a shared folder"); - + if (item.getOwner().equals(user)) throw new UserNotAuthorizedException("owner acl cannot be changed"); - + authChecker.checkAdministratorControl(ses, (SharedFolder) item); SharedFolder folder = ((SharedFolder)item); if (folder.isVreFolder()) { - if (accessType!=AccessType.ADMINISTRATOR) - throw new InvalidCallParameters("acls in vreFolder cannot be changed, only new admin can be set"); - - org.apache.jackrabbit.api.security.user.UserManager usrManager = ((JackrabbitSession)ses).getUserManager(); - - String groupId = folder.getTitle(); - - Group group = (Group)usrManager.getAuthorizable(groupId); - User authUser = (User)usrManager.getAuthorizable(user); - - if (!group.isMember(authUser)) - throw new InvalidCallParameters("user "+user+" is not in the group "+groupId); - - String path = node.getPath(); - AccessControlManager acm = ses.getAccessControlManager(); - JackrabbitAccessControlList acls = AccessControlUtils.getAccessControlList(acm, path); - Privilege[] userPrivileges = new Privilege[] { acm.privilegeFromName(AccessType.ADMINISTRATOR.getValue()) }; - Principal principal = AccessControlUtils.getPrincipal(ses, user); - acls.addAccessControlEntry(principal, userPrivileges); - acm.setPolicy(path, acls); - ses.save(); + throw new InvalidCallParameters("acls in vreFolder cannot be changed with this method"); } else { @@ -234,14 +212,14 @@ public class ACLManager { * * * @param String user - * @param accessType accessType + * * * @exception {@link RepositoryException} when a generic jcr error occurs * @exception {@link UserNotAuthorizedException} when the caller is not ADMINISTRATOR of the shared folder * @exception {@link InvalidCallParameters} when the folder is not shared with the specified user * @exception {@link InvalidItemException} when the folder is not share */ - /*@DELETE + @DELETE @Consumes(MediaType.TEXT_PLAIN) @Path("{id}/acls/{user}") public void removeACL(@PathParam("user") String user) { @@ -254,35 +232,37 @@ public class ACLManager { Item item = node2Item.getItem(node, Excludes.ALL); + if (!(item instanceof SharedFolder)) throw new InvalidItemException("the item is not a shared folder"); - + + if (item instanceof VreFolder) + throw new InvalidCallParameters("acls in vreFolder cannot be changed with this method"); + authChecker.checkAdministratorControl(ses, (SharedFolder) item); SharedFolder folder = ((SharedFolder)item); - if (folder.isVreFolder()) { - AccessControlManager acm = ses.getAccessControlManager(); - JackrabbitAccessControlList acls = AccessControlUtils.getAccessControlList(acm, folder.getPath()); + AccessControlManager acm = ses.getAccessControlManager(); + JackrabbitAccessControlList acls = AccessControlUtils.getAccessControlList(acm, folder.getPath()); - AccessControlEntry entryToDelete= null; - for (AccessControlEntry ace :acls.getAccessControlEntries()) { - if (ace.getPrincipal().getName().equals(user)) { - entryToDelete = ace; - break; - } - + AccessControlEntry entryToDelete= null; + for (AccessControlEntry ace :acls.getAccessControlEntries()) { + if (ace.getPrincipal().getName().equals(user)) { + entryToDelete = ace; + break; } - if (entryToDelete!=null) - acls.removeAccessControlEntry(entryToDelete); - else return; - - acm.setPolicy(folder.getPath(), acls); - ses.save(); - log.debug("removed Access control entry for user {}",user); - } else throw new InvalidCallParameters("remove acl can be called only on VRE folder"); + } + if (entryToDelete!=null) + acls.removeAccessControlEntry(entryToDelete); + else return; + + acm.setPolicy(folder.getPath(), acls); + ses.save(); + log.debug("removed Access control entry for user {}",user); + }catch(RepositoryException re){ log.error("jcr error extracting archive", re); @@ -294,7 +274,7 @@ public class ACLManager { if (ses!=null) ses.logout(); } - }*/ + } @GET @Path("{id}/acls/write") diff --git a/src/main/java/org/gcube/data/access/storagehub/services/GroupManager.java b/src/main/java/org/gcube/data/access/storagehub/services/GroupManager.java index cbee116..25e4279 100644 --- a/src/main/java/org/gcube/data/access/storagehub/services/GroupManager.java +++ b/src/main/java/org/gcube/data/access/storagehub/services/GroupManager.java @@ -1,5 +1,6 @@ package org.gcube.data.access.storagehub.services; +import java.security.Principal; import java.util.ArrayList; import java.util.Iterator; import java.util.List; @@ -8,6 +9,8 @@ import javax.inject.Inject; import javax.jcr.Node; import javax.jcr.NodeIterator; import javax.jcr.PathNotFoundException; +import javax.jcr.RepositoryException; +import javax.jcr.security.AccessControlEntry; import javax.jcr.security.AccessControlManager; import javax.jcr.security.Privilege; import javax.servlet.ServletContext; @@ -22,6 +25,7 @@ import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; import org.apache.jackrabbit.api.JackrabbitSession; import org.apache.jackrabbit.api.security.JackrabbitAccessControlList; @@ -34,15 +38,26 @@ import org.apache.jackrabbit.commons.jackrabbit.authorization.AccessControlUtils import org.gcube.common.authorization.control.annotations.AuthorizationControl; import org.gcube.common.authorization.library.provider.AuthorizationProvider; import org.gcube.common.gxrest.response.outbound.GXOutboundErrorResponse; +import org.gcube.common.scope.api.ScopeProvider; +import org.gcube.common.storagehub.model.Excludes; import org.gcube.common.storagehub.model.acls.AccessType; import org.gcube.common.storagehub.model.exceptions.BackendGenericError; +import org.gcube.common.storagehub.model.exceptions.InvalidCallParameters; import org.gcube.common.storagehub.model.exceptions.InvalidItemException; +import org.gcube.common.storagehub.model.exceptions.StorageHubException; +import org.gcube.common.storagehub.model.exceptions.UserNotAuthorizedException; import org.gcube.common.storagehub.model.types.NodeProperty; import org.gcube.common.storagehub.model.types.PrimaryNodeType; +import org.gcube.data.access.storagehub.AuthorizationChecker; import org.gcube.data.access.storagehub.Constants; import org.gcube.data.access.storagehub.Utils; import org.gcube.data.access.storagehub.exception.MyAuthException; import org.gcube.data.access.storagehub.handlers.CredentialHandler; +import org.gcube.data.access.storagehub.handlers.Node2ItemConverter; +import org.gcube.data.access.storagehub.handlers.VRE; +import org.gcube.data.access.storagehub.handlers.VREManager; +import org.gcube.smartgears.utils.InnerMethodName; +import org.glassfish.jersey.media.multipart.FormDataParam; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -51,17 +66,30 @@ public class GroupManager { @Context ServletContext context; + private static final String VREMANAGER_ROLE = "VRE-Manager"; + private static final String INFRASTRUCTURE_MANAGER_ROLE = "Infrastructure-Manager"; + private static final Logger log = LoggerFactory.getLogger(GroupManager.class); @Inject RepositoryInitializer repository; + @Inject + VREManager vreManager; + + @Inject + Node2ItemConverter node2Item; + + @Inject + AuthorizationChecker authChecker; + @GET @Path("") @Produces(MediaType.APPLICATION_JSON) - @AuthorizationControl(allowed={"lucio.lelii"}, exception=MyAuthException.class) public List getGroups(){ + InnerMethodName.instance.set("getGroups"); + JackrabbitSession session = null; List groups= new ArrayList<>(); try { @@ -80,9 +108,9 @@ public class GroupManager { log.info("group {} found",group.getPrincipal().getName()); groups.add(group.getPrincipal().getName()); } - }catch(Exception e) { - log.error("jcr error getting groups", e); - GXOutboundErrorResponse.throwException(new BackendGenericError(e)); + }catch(RepositoryException re ){ + log.error("jcr error creating item", re); + GXOutboundErrorResponse.throwException(new BackendGenericError("jcr error creating item", re)); } finally { if (session!=null) session.logout(); @@ -92,13 +120,16 @@ public class GroupManager { @POST @Path("") - @Consumes(MediaType.APPLICATION_FORM_URLENCODED) - @AuthorizationControl(allowed={"lucio.lelii"}, exception=MyAuthException.class) - public String createGroup(@FormParam("group") String group, @FormParam("accessType") AccessType accessType){ + @Consumes(MediaType.MULTIPART_FORM_DATA) + @AuthorizationControl(allowedRoles={INFRASTRUCTURE_MANAGER_ROLE}, exception=MyAuthException.class) + public String createGroup(@FormDataParam("group") String group, @FormDataParam("accessType") AccessType accessType, @FormDataParam("folderOwner") String folderOwner){ + + InnerMethodName.instance.set("createGroup"); JackrabbitSession session = null; String groupId = null; try { + session = (JackrabbitSession) repository.getRepository().login(CredentialHandler.getAdminCredentials(context)); org.apache.jackrabbit.api.security.user.UserManager usrManager = session.getUserManager(); @@ -106,7 +137,7 @@ public class GroupManager { Group createdGroup = usrManager.createGroup(group); groupId = createdGroup.getID(); - createVreFolder(groupId, session, accessType!=null?accessType:AccessType.WRITE_OWNER); + createVreFolder(groupId, session, accessType!=null?accessType:AccessType.WRITE_OWNER, folderOwner); session.save(); }catch(Exception e) { @@ -122,11 +153,14 @@ public class GroupManager { @DELETE @Path("{group}") - @AuthorizationControl(allowed={"lucio.lelii"}, exception=MyAuthException.class) + @AuthorizationControl(allowedRoles={INFRASTRUCTURE_MANAGER_ROLE}, exception=MyAuthException.class) public String deleteGroup(@PathParam("group") String group){ + InnerMethodName.instance.set("deleteGroup"); + JackrabbitSession session = null; try { + session = (JackrabbitSession) repository.getRepository().login(CredentialHandler.getAdminCredentials(context)); org.apache.jackrabbit.api.security.user.UserManager usrManager = session.getUserManager(); @@ -140,26 +174,169 @@ public class GroupManager { if (authorizable.isGroup()) authorizable.remove(); session.save(); - }catch(Exception e) { - log.error("jcr error getting users", e); - GXOutboundErrorResponse.throwException(new BackendGenericError(e)); + }catch(RepositoryException re ){ + log.error("jcr error creating item", re); + GXOutboundErrorResponse.throwException(new BackendGenericError("jcr error creating item", re)); } finally { if (session!=null) session.logout(); } - return group; } + + public boolean isInfraManager() { return AuthorizationProvider.instance.get().getClient().getRoles().contains(INFRASTRUCTURE_MANAGER_ROLE); } + @PUT - @Path("{id}") + @Path("{id}/admins") @Consumes(MediaType.APPLICATION_FORM_URLENCODED) - @AuthorizationControl(allowed={"lucio.lelii"}, exception=MyAuthException.class) + @AuthorizationControl(allowedRoles={VREMANAGER_ROLE, INFRASTRUCTURE_MANAGER_ROLE}, exception=MyAuthException.class) + public void addAdmin(@PathParam("id") String groupId, @FormParam("userId") String userId){ + + InnerMethodName.instance.set("addAddmin"); + + JackrabbitSession session = null; + try { + + if (!isInfraManager() && !isValidGroupForContext(groupId) ) + throw new UserNotAuthorizedException("only VREManager of the selected VRE can execute this operation"); + + session = (JackrabbitSession) repository.getRepository().login(CredentialHandler.getAdminCredentials(context)); + + + org.apache.jackrabbit.api.security.user.UserManager usrManager = ((JackrabbitSession)session).getUserManager(); + + + Group group = (Group)usrManager.getAuthorizable(groupId); + User authUser = (User)usrManager.getAuthorizable(userId); + + if (!group.isMember(authUser)) + throw new InvalidCallParameters("user "+userId+" is not in the group "+groupId); + + Node vreFolder = getVreFolderNode(session, groupId); + AccessControlManager acm = session.getAccessControlManager(); + JackrabbitAccessControlList acls = AccessControlUtils.getAccessControlList(acm, vreFolder.getPath()); + Privilege[] userPrivileges = new Privilege[] { acm.privilegeFromName(AccessType.ADMINISTRATOR.getValue()) }; + Principal principal = AccessControlUtils.getPrincipal(session, userId); + acls.addAccessControlEntry(principal, userPrivileges); + acm.setPolicy(vreFolder.getPath(), acls); + + session.save(); + }catch(StorageHubException she ){ + log.error(she.getErrorMessage(), she); + GXOutboundErrorResponse.throwException(she, Response.Status.fromStatusCode(she.getStatus())); + }catch(RepositoryException re ){ + log.error("adding admin to VREFolder", re); + GXOutboundErrorResponse.throwException(new BackendGenericError("jcr error adding admin to VREFolder", re)); + } finally { + if (session!=null) + session.logout(); + } + } + + @DELETE + @Path("{id}/admins/{userId}") + @Consumes(MediaType.APPLICATION_FORM_URLENCODED) + @AuthorizationControl(allowedRoles={VREMANAGER_ROLE, INFRASTRUCTURE_MANAGER_ROLE}, exception=MyAuthException.class) + public void removeAdmin(@PathParam("id") String groupId, @PathParam("userId") String userId){ + + InnerMethodName.instance.set("removeAdmin"); + + JackrabbitSession session = null; + + try { + + if (!isValidGroupForContext(groupId) && !isInfraManager()) + throw new UserNotAuthorizedException("only VREManager of the selected VRE can execute this operation"); + + session = (JackrabbitSession) repository.getRepository().login(CredentialHandler.getAdminCredentials(context)); + + + Node vreFolder = getVreFolderNode(session, groupId); + AccessControlManager acm = session.getAccessControlManager(); + JackrabbitAccessControlList acls = AccessControlUtils.getAccessControlList(acm, vreFolder.getPath()); + + AccessControlEntry toRemove = null; + for (AccessControlEntry acl: acls.getAccessControlEntries()) + if (acl.getPrincipal().getName().equals(userId)) { + toRemove = acl; + break; + } + + acls.removeAccessControlEntry(toRemove); + acm.setPolicy(vreFolder.getPath(), acls); + session.save(); + }catch(StorageHubException she ){ + log.error(she.getErrorMessage(), she); + GXOutboundErrorResponse.throwException(she, Response.Status.fromStatusCode(she.getStatus())); + }catch(RepositoryException re ){ + log.error("jcr error creating item", re); + GXOutboundErrorResponse.throwException(new BackendGenericError("jcr error creating item", re)); + }finally { + if (session!=null) + session.logout(); + } + } + + @GET + @Path("{groupId}/admins") + @Produces(MediaType.APPLICATION_JSON) + public List getAdmins(@PathParam("groupId") String groupId){ + + InnerMethodName.instance.set("getAdmins"); + + JackrabbitSession session = null; + List users = new ArrayList<>(); + try { + + session = (JackrabbitSession) repository.getRepository().login(CredentialHandler.getAdminCredentials(context)); + + session = (JackrabbitSession) repository.getRepository().login(CredentialHandler.getAdminCredentials(context)); + VRE vreFolder = Utils.getVreFolderItemByGroupNameAndUser(session, groupId, AuthorizationProvider.instance.get().getClient().getId(), node2Item, vreManager, Excludes.ALL); + AccessControlManager acm = session.getAccessControlManager(); + //authChecker.checkAdministratorControl(session, (VreFolder)vreFolder.getVreFolder()); + Node node = session.getNodeByIdentifier(vreFolder.getVreFolder().getId()); + + JackrabbitAccessControlList acls = AccessControlUtils.getAccessControlList(acm, node.getPath()); + + for (AccessControlEntry acl: acls.getAccessControlEntries()) + for (Privilege pr: acl.getPrivileges()) { + if (pr.getName().equals(AccessType.ADMINISTRATOR.getValue())){ + users.add(acl.getPrincipal().getName()); + } + + } + + }catch(StorageHubException she ){ + log.error(she.getErrorMessage(), she); + GXOutboundErrorResponse.throwException(she, Response.Status.fromStatusCode(she.getStatus())); + }catch(RepositoryException re ){ + log.error("jcr error creating item", re); + GXOutboundErrorResponse.throwException(new BackendGenericError("jcr error creating item", re)); + }finally { + if (session!=null) + session.logout(); + } + + return users; + } + + + @PUT + @Path("{id}/users") + @Consumes(MediaType.APPLICATION_FORM_URLENCODED) + @AuthorizationControl(allowedRoles={VREMANAGER_ROLE, INFRASTRUCTURE_MANAGER_ROLE}, exception=MyAuthException.class) public boolean addUserToGroup(@PathParam("id") String groupId, @FormParam("userId") String userId){ + InnerMethodName.instance.set("addUserToGroup"); + JackrabbitSession session = null; boolean success = false; try { + + if (!isValidGroupForContext(groupId) && !isInfraManager()) + throw new UserNotAuthorizedException("only VREManager of the selected VRE can execute this operation"); + session = (JackrabbitSession) repository.getRepository().login(CredentialHandler.getAdminCredentials(context)); org.apache.jackrabbit.api.security.user.UserManager usrManager = session.getUserManager(); @@ -167,6 +344,9 @@ public class GroupManager { Group group = (Group)usrManager.getAuthorizable(groupId); User user = (User)usrManager.getAuthorizable(userId); + if (group.isMember(user)) + throw new InvalidCallParameters("user "+userId+" is already member of group "+groupId); + success = group.addMember(user); String folderName = group.getPrincipal().getName(); @@ -177,10 +357,13 @@ public class GroupManager { session.getWorkspace().clone(session.getWorkspace().getName(), folder.getPath(),userPath , false); session.save(); - }catch(Exception e) { - log.error("jcr error adding user {} to group {}", userId, groupId, e); - GXOutboundErrorResponse.throwException(new BackendGenericError(e)); - } finally { + }catch(StorageHubException she ){ + log.error(she.getErrorMessage(), she); + GXOutboundErrorResponse.throwException(she, Response.Status.fromStatusCode(she.getStatus())); + }catch(RepositoryException re ){ + log.error("jcr error creating item", re); + GXOutboundErrorResponse.throwException(new BackendGenericError("jcr error creating item", re)); + }finally { if (session!=null) session.logout(); } @@ -190,12 +373,18 @@ public class GroupManager { @DELETE @Path("{groupId}/users/{userId}") - @AuthorizationControl(allowed={"lucio.lelii"}, exception=MyAuthException.class) + @AuthorizationControl(allowedRoles={VREMANAGER_ROLE, INFRASTRUCTURE_MANAGER_ROLE}, exception=MyAuthException.class) public boolean removeUserFromGroup(@PathParam("groupId") String groupId, @PathParam("userId") String userId){ + InnerMethodName.instance.set("removeUserFromGroup"); + JackrabbitSession session = null; boolean success = false; try { + + if (!isValidGroupForContext(groupId) && !isInfraManager()) + throw new UserNotAuthorizedException("only VREManager of the selected VRE can execute this operation"); + session = (JackrabbitSession) repository.getRepository().login(CredentialHandler.getAdminCredentials(context)); org.apache.jackrabbit.api.security.user.UserManager usrManager = session.getUserManager(); @@ -203,6 +392,9 @@ public class GroupManager { Group group = (Group)usrManager.getAuthorizable(groupId); User user = (User)usrManager.getAuthorizable(userId); + if (!group.isMember(user)) + throw new InvalidCallParameters("user "+userId+" is not member of group "+groupId); + //delete folder on user String folderName = group.getPrincipal().getName(); Node folder = getVreFolderNode(session, folderName); @@ -219,9 +411,12 @@ public class GroupManager { success = group.removeMember(user); session.save(); - }catch(Exception e) { - log.error("jcr error adding user {} to group {}", userId, groupId, e); - GXOutboundErrorResponse.throwException(new BackendGenericError(e)); + }catch(StorageHubException she ){ + log.error(she.getErrorMessage(), she); + GXOutboundErrorResponse.throwException(she, Response.Status.fromStatusCode(she.getStatus())); + }catch(RepositoryException re ){ + log.error("jcr error creating item", re); + GXOutboundErrorResponse.throwException(new BackendGenericError("jcr error creating item", re)); } finally { if (session!=null) session.logout(); @@ -233,12 +428,18 @@ public class GroupManager { @GET @Path("{groupId}/users") @Produces(MediaType.APPLICATION_JSON) - @AuthorizationControl(allowed={"lucio.lelii"}, exception=MyAuthException.class) + @AuthorizationControl(allowedRoles={VREMANAGER_ROLE}, exception=MyAuthException.class) public List getUsersOfGroup(@PathParam("groupId") String groupId){ + InnerMethodName.instance.set("getUsersOfGroup"); + JackrabbitSession session = null; List users = new ArrayList<>(); try { + + if (!isValidGroupForContext(groupId)) + throw new UserNotAuthorizedException("only VREManager of the selected VRE can execute this operation"); + session = (JackrabbitSession) repository.getRepository().login(CredentialHandler.getAdminCredentials(context)); org.apache.jackrabbit.api.security.user.UserManager usrManager = session.getUserManager(); @@ -253,10 +454,13 @@ public class GroupManager { } - }catch(Exception e) { - log.error("jcr error getting users of group {}", groupId, e); - GXOutboundErrorResponse.throwException(new BackendGenericError(e)); - } finally { + }catch(StorageHubException she ){ + log.error(she.getErrorMessage(), she); + GXOutboundErrorResponse.throwException(she, Response.Status.fromStatusCode(she.getStatus())); + }catch(RepositoryException re ){ + log.error("jcr error creating item", re); + GXOutboundErrorResponse.throwException(new BackendGenericError("jcr error creating item", re)); + }finally { if (session!=null) session.logout(); } @@ -264,7 +468,7 @@ public class GroupManager { return users; } - private void createVreFolder(String groupId, JackrabbitSession session, AccessType defaultAccessType) throws Exception{ + private void createVreFolder(String groupId, JackrabbitSession session, AccessType defaultAccessType, String owner ) throws Exception{ Node sharedRootNode = session.getNode(Constants.SHARED_FOLDER_PATH); @@ -272,7 +476,7 @@ public class GroupManager { String title = groupId.substring(groupId.lastIndexOf("-")+1); - Node folder= Utils.createFolderInternally(session, sharedRootNode, name, "VREFolder for "+groupId, false, AuthorizationProvider.instance.get().getClient().getId(), null); + Node folder= Utils.createFolderInternally(session, sharedRootNode, name, "VREFolder for "+groupId, false, owner, null); folder.setPrimaryType(PrimaryNodeType.NT_WORKSPACE_SHARED_FOLDER); folder.setProperty(NodeProperty.IS_VRE_FOLDER.toString(), true); folder.setProperty(NodeProperty.TITLE.toString(), name); @@ -281,25 +485,29 @@ public class GroupManager { AccessControlManager acm = session.getAccessControlManager(); JackrabbitAccessControlList acls = AccessControlUtils.getAccessControlList(acm, folder.getPath()); - Privilege[] adminPrivileges = new Privilege[] { acm.privilegeFromName(AccessType.ADMINISTRATOR.getValue()) }; + + + /*Privilege[] adminPrivileges = new Privilege[] { acm.privilegeFromName(AccessType.ADMINISTRATOR.getValue()) }; acls.addAccessControlEntry(AccessControlUtils.getPrincipal(session, AuthorizationProvider.instance.get().getClient().getId()), adminPrivileges ); + */ + Privilege[] usersPrivileges = new Privilege[] { acm.privilegeFromName(defaultAccessType.getValue()) }; acls.addAccessControlEntry(AccessControlUtils.getPrincipal(session,groupId), usersPrivileges ); acm.setPolicy(folder.getPath(), acls); } - - private Node getVreFolderNode(JackrabbitSession session, String name) throws InvalidItemException, Exception { + + private Node getVreFolderNode(JackrabbitSession session, String name) throws InvalidItemException, RepositoryException { Node sharedRootNode = session.getNode(Constants.SHARED_FOLDER_PATH); - + Node vreFolder = null; try { vreFolder = sharedRootNode.getNode(name); }catch (PathNotFoundException e) { log.debug("is an old HL VRE"); } - + NodeIterator nodes = sharedRootNode.getNodes(); while (nodes.hasNext()) { Node node = nodes.nextNode(); @@ -313,4 +521,10 @@ public class GroupManager { return vreFolder; } + private boolean isValidGroupForContext(String group){ + String currentContext = ScopeProvider.instance.get(); + String expectedGroupId= currentContext.replace("/", "-").substring(1); + return group.equals(expectedGroupId); + } + } diff --git a/src/main/java/org/gcube/data/access/storagehub/services/ItemsCreator.java b/src/main/java/org/gcube/data/access/storagehub/services/ItemsCreator.java index e669bdd..4e00d42 100644 --- a/src/main/java/org/gcube/data/access/storagehub/services/ItemsCreator.java +++ b/src/main/java/org/gcube/data/access/storagehub/services/ItemsCreator.java @@ -192,6 +192,7 @@ public class ItemsCreator { ses.getWorkspace().getLockManager().unlock(destination.getPath()); } + log.info("item with id {} correctly created",newNode.getIdentifier()); toReturn = newNode.getIdentifier(); }catch(StorageHubException she ){ @@ -354,12 +355,13 @@ public class ItemsCreator { } try { newNode = item2Node.getNode(destinationNode, item); + accountingHandler.createEntryCreate(item.getTitle(), ses, newNode, false); ses.save(); }finally { if (withLock) ses.getWorkspace().getLockManager().unlock(destinationNode.getPath()); } versionHandler.makeVersionableContent(newNode, ses); - accountingHandler.createFolderAddObj(name, item.getClass().getSimpleName(), item.getContent().getMimeType(), ses, newNode, false); + accountingHandler.createFolderAddObj(name, item.getClass().getSimpleName(), item.getContent().getMimeType(), ses, destinationNode, false); } return newNode; diff --git a/src/main/java/org/gcube/data/access/storagehub/services/ItemsManager.java b/src/main/java/org/gcube/data/access/storagehub/services/ItemsManager.java index dce3abd..9d8470b 100644 --- a/src/main/java/org/gcube/data/access/storagehub/services/ItemsManager.java +++ b/src/main/java/org/gcube/data/access/storagehub/services/ItemsManager.java @@ -250,6 +250,34 @@ public class ItemsManager { return new ItemList(toReturn); } + @GET + @Path("{id}/search") + @Produces(MediaType.APPLICATION_JSON) + public ItemList searchItems(@QueryParam("showHidden") Boolean showHidden, @QueryParam("exclude") List excludes, @QueryParam("onlyType") String nodeType,@QueryParam("name") String name ){ + InnerMethodName.instance.set("listById"); + Session ses = null; + List toReturn = null; + try{ + ses = repository.getRepository().login(CredentialHandler.getAdminCredentials(context)); + authChecker.checkReadAuthorizationControl(ses, id); + toReturn = Utils.serachByNameOnFolder(ses, ses.getNodeByIdentifier(id), excludes, null, showHidden==null?false:showHidden, nodeType!=null ? ClassHandler.instance().get(nodeType) : null, name); + }catch (ItemNotFoundException e) { + log.error("id {} not found",id,e); + GXOutboundErrorResponse.throwException(new IdNotFoundException(id, e), Status.NOT_FOUND); + }catch(RepositoryException re){ + log.error("jcr error getting children", re); + GXOutboundErrorResponse.throwException(new BackendGenericError(re)); + }catch(StorageHubException she ){ + log.error(she.getErrorMessage(), she); + GXOutboundErrorResponse.throwException(she, Response.Status.fromStatusCode(she.getStatus())); + }finally{ + if (ses!=null) + ses.logout(); + } + + return new ItemList(toReturn); + } + @GET @Path("{id}/children/paged") @Produces(MediaType.APPLICATION_JSON) @@ -280,7 +308,7 @@ public class ItemsManager { @GET @Path("publiclink/{id}") - @AuthorizationControl(allowed={"URIResolver"}, exception=MyAuthException.class) + @AuthorizationControl(allowedUsers={"URIResolver"}, exception=MyAuthException.class) public Response resolvePublicLink() { InnerMethodName.instance.set("resolvePubliclink"); diff --git a/src/main/java/org/gcube/data/access/storagehub/services/UserManager.java b/src/main/java/org/gcube/data/access/storagehub/services/UserManager.java index 4b59a63..c250505 100644 --- a/src/main/java/org/gcube/data/access/storagehub/services/UserManager.java +++ b/src/main/java/org/gcube/data/access/storagehub/services/UserManager.java @@ -8,6 +8,8 @@ import java.util.List; import javax.inject.Inject; import javax.jcr.Node; import javax.jcr.NodeIterator; +import javax.jcr.PathNotFoundException; +import javax.jcr.RepositoryException; import javax.jcr.query.QueryResult; import javax.servlet.ServletContext; import javax.ws.rs.Consumes; @@ -20,6 +22,7 @@ import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; import org.apache.jackrabbit.api.JackrabbitSession; import org.apache.jackrabbit.api.security.user.Authorizable; @@ -28,22 +31,28 @@ import org.apache.jackrabbit.api.security.user.QueryBuilder; import org.apache.jackrabbit.api.security.user.User; import org.apache.jackrabbit.core.security.principal.PrincipalImpl; import org.gcube.common.authorization.control.annotations.AuthorizationControl; +import org.gcube.common.authorization.library.provider.AuthorizationProvider; import org.gcube.common.gxrest.response.outbound.GXOutboundErrorResponse; import org.gcube.common.storagehub.model.exceptions.BackendGenericError; +import org.gcube.common.storagehub.model.exceptions.StorageHubException; +import org.gcube.common.storagehub.model.exceptions.UserNotAuthorizedException; import org.gcube.common.storagehub.model.types.NodeProperty; import org.gcube.data.access.storagehub.Constants; import org.gcube.data.access.storagehub.Utils; import org.gcube.data.access.storagehub.exception.MyAuthException; import org.gcube.data.access.storagehub.handlers.CredentialHandler; import org.gcube.data.access.storagehub.handlers.UnshareHandler; +import org.gcube.smartgears.utils.InnerMethodName; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @Path("users") public class UserManager { + private static final String INFRASTRUCTURE_MANAGER_ROLE = "Infrastructure-Manager"; + @Context ServletContext context; - + private static final Logger log = LoggerFactory.getLogger(UserManager.class); @Inject @@ -55,9 +64,10 @@ public class UserManager { @GET @Path("") @Produces(MediaType.APPLICATION_JSON) - @AuthorizationControl(allowed={"lucio.lelii"}, exception=MyAuthException.class) public List getUsers(){ - + + InnerMethodName.instance.set("getUsers"); + JackrabbitSession session = null; List users= new ArrayList<>(); try { @@ -89,14 +99,17 @@ public class UserManager { @POST @Path("") @Consumes(MediaType.APPLICATION_FORM_URLENCODED) - @AuthorizationControl(allowed={"lucio.lelii"}, exception=MyAuthException.class) + @AuthorizationControl(allowedRoles={INFRASTRUCTURE_MANAGER_ROLE}, exception=MyAuthException.class) public String createUser(@FormParam("user") String user, @FormParam("password") String password){ - + + InnerMethodName.instance.set("createUser"); + JackrabbitSession session = null; String userId = null; try { session = (JackrabbitSession) repository.getRepository().login(CredentialHandler.getAdminCredentials(context)); - + + org.apache.jackrabbit.api.security.user.UserManager usrManager = session.getUserManager(); User createdUser = usrManager.createUser(user, password); @@ -113,9 +126,12 @@ public class UserManager { Utils.createFolderInternally(session, workspaceFolder, Constants.VRE_FOLDER_PARENT_NAME, "special folder container of "+user, false, user, null); session.save(); - }catch(Exception e) { - log.error("jcr error creating user {}", user, e); - GXOutboundErrorResponse.throwException(new BackendGenericError(e)); + }catch(StorageHubException she ){ + log.error(she.getErrorMessage(), she); + GXOutboundErrorResponse.throwException(she, Response.Status.fromStatusCode(she.getStatus())); + }catch(RepositoryException re ){ + log.error("jcr error creating item", re); + GXOutboundErrorResponse.throwException(new BackendGenericError("jcr error creating item", re)); } finally { if (session!=null) session.logout(); @@ -126,18 +142,21 @@ public class UserManager { @DELETE - @Path("{id}") - @AuthorizationControl(allowed={"lucio.lelii"}, exception=MyAuthException.class) - public String deleteUser(@PathParam("id") String id){ - + @Path("{user}") + @AuthorizationControl(allowedRoles={INFRASTRUCTURE_MANAGER_ROLE}, exception=MyAuthException.class) + public String deleteUser(@PathParam("user") String user){ + + InnerMethodName.instance.set("deleteUser"); + JackrabbitSession session = null; String userId = null; try { + session = (JackrabbitSession) repository.getRepository().login(CredentialHandler.getAdminCredentials(context)); org.apache.jackrabbit.api.security.user.UserManager usrManager = session.getUserManager(); - org.gcube.common.storagehub.model.Path path = Utils.getWorkspacePath(id); + org.gcube.common.storagehub.model.Path path = Utils.getWorkspacePath(user); String sql2Query = String.format("SELECT * FROM [nthl:workspaceSharedItem] AS node WHERE ISDESCENDANTNODE('%s')", path.toPath()); @@ -153,18 +172,28 @@ public class UserManager { Node rNode = nodeIt.nextNode(); String title = rNode.hasProperty(NodeProperty.TITLE.toString()) ? rNode.getProperty(NodeProperty.TITLE.toString()).getString():"unknown"; log.debug("removing sharing for folder name {} with title {} and path {} ",rNode.getName(), title, rNode.getPath()); - unshareHandler.unshare(session, Collections.singleton(id), rNode, id); + unshareHandler.unshare(session, Collections.singleton(user), rNode, user); } - Authorizable authorizable = usrManager.getAuthorizable(new PrincipalImpl(id)); - if (!authorizable.isGroup()) { - log.info("removing user {}", id); + Authorizable authorizable = usrManager.getAuthorizable(new PrincipalImpl(user)); + if (authorizable!=null && !authorizable.isGroup()) { + log.info("removing user {}", user); authorizable.remove(); + } else log.warn("the user {} was already deleted", user); + + org.gcube.common.storagehub.model.Path homePath = Utils.getHome(user); + try { + session.getNode(homePath.toPath()).remove(); + } catch (PathNotFoundException e) { + log.warn("{} home dir was already deleted", user); } session.save(); - }catch(Exception e) { - log.error("jcr error getting users", e); - GXOutboundErrorResponse.throwException(new BackendGenericError(e)); + }catch(StorageHubException she ){ + log.error(she.getErrorMessage(), she); + GXOutboundErrorResponse.throwException(she, Response.Status.fromStatusCode(she.getStatus())); + }catch(RepositoryException re ){ + log.error("jcr error creating item", re); + GXOutboundErrorResponse.throwException(new BackendGenericError("jcr error creating item", re)); } finally { if (session!=null) session.logout(); diff --git a/src/main/java/org/gcube/data/access/storagehub/services/WorkspaceManager.java b/src/main/java/org/gcube/data/access/storagehub/services/WorkspaceManager.java index 6168e7e..2f9fa10 100644 --- a/src/main/java/org/gcube/data/access/storagehub/services/WorkspaceManager.java +++ b/src/main/java/org/gcube/data/access/storagehub/services/WorkspaceManager.java @@ -136,7 +136,7 @@ public class WorkspaceManager { return new ItemWrapper(toReturn); } - private synchronized VRE getVreFolderItem(Session ses) throws RepositoryException, BackendGenericError{ + /*private synchronized VRE getVreFolderItem(Session ses) throws RepositoryException, BackendGenericError{ org.gcube.common.storagehub.model.Path vrePath = Paths.append(Utils.getWorkspacePath(), Constants.VRE_FOLDER_PARENT_NAME); ScopeBean bean = new ScopeBean(ScopeProvider.instance.get()); if (!bean.is(Type.VRE)) throw new BackendGenericError("the current scope is not a VRE"); @@ -155,7 +155,7 @@ public class WorkspaceManager { return vreManager.putVRE(vreFolder); } - } + }*/ @Path("vrefolder") @@ -167,7 +167,7 @@ public class WorkspaceManager { Item vreItem = null; try { ses = repository.getRepository().login(CredentialHandler.getAdminCredentials(context)); - vreItem = getVreFolderItem(ses).getVreFolder(); + vreItem = Utils.getVreFolderItem(ses, node2Item, vreManager, excludes).getVreFolder(); }catch(RepositoryException re ){ log.error("jcr error getting vrefolder", re); GXOutboundErrorResponse.throwException(new BackendGenericError(re)); @@ -192,7 +192,7 @@ public class WorkspaceManager { String login = AuthorizationProvider.instance.get().getClient().getId(); ses = repository.getRepository().login(CredentialHandler.getAdminCredentials(context)); - VRE vre = getVreFolderItem(ses); + VRE vre = Utils.getVreFolderItem(ses, node2Item, vreManager, excludes); log.trace("VRE retrieved {}",vre.getVreFolder().getTitle()); recentItems = vre.getRecents(); log.trace("recents retrieved {}",vre.getVreFolder().getTitle()); diff --git a/src/main/webapp/WEB-INF/README b/src/main/webapp/WEB-INF/README index 312b865..e4fa25e 100644 --- a/src/main/webapp/WEB-INF/README +++ b/src/main/webapp/WEB-INF/README @@ -25,7 +25,7 @@ The projects leading to this software have received funding from a series of Version -------------------------------------------------- -1.0.8 (2020-01-22) +1.0.9 (2020-01-22) Please see the file named "changelog.xml" in this directory for the release notes. diff --git a/src/main/webapp/WEB-INF/gcube-app.xml b/src/main/webapp/WEB-INF/gcube-app.xml index 4f0d8e1..19885b9 100644 --- a/src/main/webapp/WEB-INF/gcube-app.xml +++ b/src/main/webapp/WEB-INF/gcube-app.xml @@ -1,7 +1,7 @@ StorageHub DataAccess - 1.0.0-SNAPSHOT + 1.0.9 Storage Hub webapp \ No newline at end of file