diff --git a/pom.xml b/pom.xml index 71c8026..38baad5 100644 --- a/pom.xml +++ b/pom.xml @@ -52,18 +52,23 @@ - + - org.aspectj - aspectjrt - provided + org.aspectj + aspectjrt - + org.gcube.core common-smartgears + + org.gcube.common + authorization-control-library + [1.0.0-SNAPSHOT,2.0.0-SNAPSHOT) + + org.gcube.common common-authorization @@ -84,6 +89,11 @@ common-scope + + org.gcube.core + common-encryption + + org.gcube.common storagehub-model @@ -331,7 +341,7 @@ org.codehaus.mojo aspectj-maven-plugin - + [1.0,) test-compile @@ -357,7 +367,7 @@ org.gcube.common - common-authorization + authorization-control-library 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 754429e..748ce56 100644 --- a/src/main/java/org/gcube/data/access/storagehub/AuthorizationChecker.java +++ b/src/main/java/org/gcube/data/access/storagehub/AuthorizationChecker.java @@ -34,7 +34,6 @@ 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.isPublicItem()) return; if (item.isShared()) { @@ -105,7 +104,7 @@ public class AuthorizationChecker { public void checkAdministratorControl(Session session, SharedFolder item) throws UserNotAuthorizedException, BackendGenericError, RepositoryException { - //TODO: riguardare qeusto pezzo di codice + //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"); 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 d22764d..1f66db9 100644 --- a/src/main/java/org/gcube/data/access/storagehub/StorageHub.java +++ b/src/main/java/org/gcube/data/access/storagehub/StorageHub.java @@ -28,6 +28,7 @@ public class StorageHub extends Application { classes.add(ACLManager.class); classes.add(ItemSharing.class); classes.add(UserManager.class); + //classes.add(AuthorizationExceptionMapper.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 14d43c4..f7a2354 100644 --- a/src/main/java/org/gcube/data/access/storagehub/Utils.java +++ b/src/main/java/org/gcube/data/access/storagehub/Utils.java @@ -112,14 +112,18 @@ public class Utils { (node.getPrimaryNodeType().getName().equals(FOLDERS_TYPE) && Constants.FOLDERS_TO_EXLUDE.contains(node.getName()))); } - public static org.gcube.common.storagehub.model.Path getHomePath(){ + public static org.gcube.common.storagehub.model.Path getWorkspacePath(){ return Paths.getPath(String.format("/Home/%s/Workspace",AuthorizationProvider.instance.get().getClient().getId())); } - public static org.gcube.common.storagehub.model.Path getHomePath(String login){ + public static org.gcube.common.storagehub.model.Path getWorkspacePath(String login){ return Paths.getPath(String.format("/Home/%s/Workspace",login)); } + public static org.gcube.common.storagehub.model.Path getHome(String login){ + return Paths.getPath(String.format("/Home/%s",login)); + } + public static StorageClient getStorageClient(String login){ return new StorageClient(SERVICE_CLASS, SERVICE_NAME, login, AccessType.SHARED, MemoryType.PERSISTENT); diff --git a/src/main/java/org/gcube/data/access/storagehub/exception/MyAuthException.java b/src/main/java/org/gcube/data/access/storagehub/exception/MyAuthException.java new file mode 100644 index 0000000..c4df950 --- /dev/null +++ b/src/main/java/org/gcube/data/access/storagehub/exception/MyAuthException.java @@ -0,0 +1,19 @@ +package org.gcube.data.access.storagehub.exception; + +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.Response.Status; + +public class MyAuthException extends WebApplicationException { + /** + * + */ + private static final long serialVersionUID = 1L; + + public MyAuthException(Throwable cause) { + super(cause, Status.FORBIDDEN); + } + + + + +} diff --git a/src/main/java/org/gcube/data/access/storagehub/handlers/TrashHandler.java b/src/main/java/org/gcube/data/access/storagehub/handlers/TrashHandler.java index 5d064ea..a1a3b93 100644 --- a/src/main/java/org/gcube/data/access/storagehub/handlers/TrashHandler.java +++ b/src/main/java/org/gcube/data/access/storagehub/handlers/TrashHandler.java @@ -49,7 +49,7 @@ public class TrashHandler { public void removeNodes(Session ses, List itemsToDelete) throws RepositoryException{ log.debug("defnitively removing nodes with ids {}",itemsToDelete); final String login = AuthorizationProvider.instance.get().getClient().getId(); - final Node trashFolder = ses.getNode(Paths.append(Utils.getHomePath(),Constants.TRASH_ROOT_FOLDER_NAME).toPath()); + final Node trashFolder = ses.getNode(Paths.append(Utils.getWorkspacePath(),Constants.TRASH_ROOT_FOLDER_NAME).toPath()); //String parentPath = itemToDelete.getParentPath(); try { ses.getWorkspace().getLockManager().lock(trashFolder.getPath(), true, true, 0,login); @@ -95,7 +95,7 @@ public class TrashHandler { public void moveToTrash(Session ses, Node nodeToDelete, Item item) throws RepositoryException, BackendGenericError{ log.debug("moving node {} to trash ",item.getId()); - final Node trashFolder = ses.getNode(Paths.append(Utils.getHomePath(),Constants.TRASH_ROOT_FOLDER_NAME).toPath()); + final Node trashFolder = ses.getNode(Paths.append(Utils.getWorkspacePath(),Constants.TRASH_ROOT_FOLDER_NAME).toPath()); final String login = AuthorizationProvider.instance.get().getClient().getId(); 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 new file mode 100644 index 0000000..b19a26d --- /dev/null +++ b/src/main/java/org/gcube/data/access/storagehub/handlers/UnshareHandler.java @@ -0,0 +1,251 @@ +package org.gcube.data.access.storagehub.handlers; + +import java.util.Calendar; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import javax.inject.Inject; +import javax.inject.Singleton; +import javax.jcr.Node; +import javax.jcr.RepositoryException; +import javax.jcr.Session; +import javax.jcr.security.AccessControlEntry; +import javax.jcr.security.AccessControlManager; + +import org.apache.jackrabbit.api.security.JackrabbitAccessControlList; +import org.apache.jackrabbit.commons.jackrabbit.authorization.AccessControlUtils; +import org.gcube.common.storagehub.model.Excludes; +import org.gcube.common.storagehub.model.NodeConstants; +import org.gcube.common.storagehub.model.exceptions.BackendGenericError; +import org.gcube.common.storagehub.model.exceptions.InvalidCallParameters; +import org.gcube.common.storagehub.model.exceptions.StorageHubException; +import org.gcube.common.storagehub.model.exceptions.UserNotAuthorizedException; +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.data.access.storagehub.AuthorizationChecker; +import org.gcube.data.access.storagehub.Utils; +import org.gcube.data.access.storagehub.accounting.AccountingHandler; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + + +@Singleton +public class UnshareHandler { + + private static final Logger log = LoggerFactory.getLogger(UnshareHandler.class); + + @Inject + AccountingHandler accountingHandler; + + @Inject + Node2ItemConverter node2Item; + + @Inject + AuthorizationChecker authChecker; + + @Inject + Item2NodeConverter item2Node; + + public String unshare(Session ses, Set users, Node sharedNode, String login) throws RepositoryException, StorageHubException{ + Item item = node2Item.getItem(sharedNode, Excludes.ALL); + if (!(item instanceof FolderItem) || !((FolderItem) item).isShared() || ((SharedFolder) item).isVreFolder()) + return null; + SharedFolder sharedItem =(SharedFolder) item; + + Set usersInSharedFolder = new HashSet<>(sharedItem.getUsers().getMap().keySet()); + usersInSharedFolder.removeAll(users); + + if (users==null || users.size()==0 || usersInSharedFolder.size()<=1) + return unshareAll(login, ses, sharedItem); + + ses.getWorkspace().getLockManager().lock(sharedNode.getPath(), true, true, 0,login); + try { + if (users.size()==1 && users.contains(login)) + return unshareCaller(login, ses, sharedItem); + else return unsharePartial(users, login, ses, sharedItem); + }finally { + ses.getWorkspace().getLockManager().unlock(sharedNode.getPath()); + } + + } + + private String unshareAll(String login, Session ses, SharedFolder item) throws StorageHubException, BackendGenericError, RepositoryException{ + + authChecker.checkAdministratorControl(ses, item); + if (!login.equals(item.getOwner())) + throw new UserNotAuthorizedException("user "+login+" not authorized to unshare all"); + + Node sharedItemNode = ses.getNodeByIdentifier(item.getId()); + + ses.getWorkspace().getLockManager().lock(sharedItemNode.getPath(), true, true, 0,login); + Node unsharedNode; + try { + log.debug("user list is empty, I'm going to remove also the shared dir"); + //TODO: take the admin folder and remove his clone then move the shared folder from share to the user home and change the folder type + String adminDirPath = (String)item.getUsers().getMap().get(login); + String[] splitString = adminDirPath.split("/"); + String parentDirectoryId = splitString[0]; + String directoryName = splitString[1]; + Node parentNode = ses.getNodeByIdentifier(parentDirectoryId); + log.debug("parent node path is {}/{}",parentNode.getPath(), directoryName); + + Node adminNode = ses.getNode(String.format("%s/%s",parentNode.getPath(), directoryName)); + adminNode.removeShare(); + + unsharedNode = createUnsharedFolder(ses, parentNode, directoryName, item.getDescription(), login); + + List itemsToCopy = Utils.getItemList(sharedItemNode, Excludes.ALL, null, true, null); + + for (Item itemCopy: itemsToCopy) { + Node itemToCopyNode = ses.getNodeByIdentifier(itemCopy.getId()); + log.debug("copying {} to {}", itemToCopyNode.getPath(), unsharedNode.getPath()); + ses.move(itemToCopyNode.getPath(), String.format("%s/%s",unsharedNode.getPath(), itemToCopyNode.getName())); + } + ses.save(); + }finally { + ses.getWorkspace().getLockManager().unlock(sharedItemNode.getPath()); + } + sharedItemNode.removeSharedSet(); + ses.save(); + log.debug("all the users have been removed, the folder is totally unshared"); + + return unsharedNode.getIdentifier(); + + } + + + + + private String unshareCaller(String login, Session ses, SharedFolder item) throws StorageHubException, RepositoryException{ + + if (login.equals(item.getOwner())) + throw new InvalidCallParameters("the callor is the owner, the folder cannot be unshared"); + + if (item.getUsers().getMap().get(login)==null) + throw new InvalidCallParameters("the folder is not shared with user "+login); + + Node sharedFolderNode =ses.getNodeByIdentifier(item.getId()); + + String parentId = removeSharingForUser(login, ses, item); + + AccessControlManager acm = ses.getAccessControlManager(); + JackrabbitAccessControlList acls = AccessControlUtils.getAccessControlList(acm, sharedFolderNode.getPath()); + + AccessControlEntry entryToDelete= null; + for (AccessControlEntry ace :acls.getAccessControlEntries()) { + if (ace.getPrincipal().getName().equals(login)) { + entryToDelete = ace; + break; + } + + } + if (entryToDelete!=null) + acls.removeAccessControlEntry(entryToDelete); + + log.debug("removed Access control entry for user {}",login); + Node sharedItemNode = ses.getNodeByIdentifier(item.getId()); + Node usersNode = sharedItemNode.getNode(NodeConstants.USERS_NAME); + usersNode.remove(); + Node newUsersNode = sharedItemNode.addNode(NodeConstants.USERS_NAME); + + item.getUsers().getMap().entrySet().stream().filter(entry -> !entry.getKey().equals(login)).forEach(entry-> {try { + newUsersNode.setProperty(entry.getKey(), (String)entry.getValue()); + } catch (Exception e) { + log.error("error adding property to shared node users node under "+item.getId()); + }}); + + acm.setPolicy(sharedFolderNode.getPath(), acls); + + ses.save(); + + return parentId; + + } + + + + + private String unsharePartial(Set usersToUnshare, String login, Session ses, SharedFolder item) throws StorageHubException, RepositoryException { + authChecker.checkAdministratorControl(ses, (SharedFolder)item); + if (usersToUnshare.contains(item.getOwner())) + throw new UserNotAuthorizedException("user "+login+" not authorized to unshare owner"); + + Node sharedFolderNode =ses.getNodeByIdentifier(item.getId()); + + AccessControlManager acm = ses.getAccessControlManager(); + JackrabbitAccessControlList acls = AccessControlUtils.getAccessControlList(acm, sharedFolderNode.getPath()); + + for (String user : usersToUnshare) { + removeSharingForUser(user, ses, item); + + AccessControlEntry entryToDelete= null; + for (AccessControlEntry ace :acls.getAccessControlEntries()) { + if (ace.getPrincipal().getName().equals(login)) { + entryToDelete = ace; + break; + } + + } + if (entryToDelete!=null) + acls.removeAccessControlEntry(entryToDelete); + } + + log.debug("removed Access control entry for user {}",login); + Node sharedItemNode = ses.getNodeByIdentifier(item.getId()); + Node usersNode = sharedItemNode.getNode(NodeConstants.USERS_NAME); + usersNode.remove(); + Node newUsersNode = sharedItemNode.addNode(NodeConstants.USERS_NAME); + + item.getUsers().getMap().entrySet().stream().filter(entry -> !usersToUnshare.contains(entry.getKey())).forEach(entry-> {try { + newUsersNode.setProperty(entry.getKey(), (String)entry.getValue()); + } catch (Exception e) { + log.error("error adding property to shared node users node under "+item.getId()); + }}); + + acm.setPolicy(sharedFolderNode.getPath(), acls); + + ses.save(); + + return item.getId(); + + } + + + private String removeSharingForUser(String user, Session ses, SharedFolder item) throws RepositoryException { + String userDirPath = (String)item.getUsers().getMap().get(user); + if (userDirPath==null) return null; + String[] splitString = userDirPath.split("/"); + String parentDirectoryId = splitString[0]; + String directoryName = splitString[1]; + 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; + } + + + private Node createUnsharedFolder(Session ses, Node destinationNode, String name, String description, String login) { + FolderItem item = new FolderItem(); + Calendar now = Calendar.getInstance(); + item.setName(name); + item.setTitle(name); + item.setDescription(description); + //item.setCreationTime(now); + item.setHidden(false); + item.setLastAction(ItemAction.CREATED); + item.setLastModificationTime(now); + item.setLastModifiedBy(login); + item.setOwner(login); + + //to inherit hidden property + //item.setHidden(destinationItem.isHidden()); + + Node newNode = item2Node.getNode(ses, destinationNode, item); + return newNode; + } +} 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 948a79e..03746ba 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 @@ -1,6 +1,5 @@ package org.gcube.data.access.storagehub.services; -import java.io.IOException; import java.util.ArrayList; import java.util.List; @@ -15,11 +14,9 @@ import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; -import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; -import org.apache.commons.compress.archivers.ArchiveException; import org.apache.jackrabbit.api.security.JackrabbitAccessControlList; import org.apache.jackrabbit.commons.jackrabbit.authorization.AccessControlUtils; import org.gcube.common.gxrest.response.outbound.GXOutboundErrorResponse; 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 new file mode 100644 index 0000000..f9a1b0e --- /dev/null +++ b/src/main/java/org/gcube/data/access/storagehub/services/GroupManager.java @@ -0,0 +1,222 @@ +package org.gcube.data.access.storagehub.services; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import javax.inject.Inject; +import javax.servlet.ServletContext; +import javax.ws.rs.DELETE; +import javax.ws.rs.FormParam; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; + +import org.apache.jackrabbit.api.JackrabbitSession; +import org.apache.jackrabbit.api.security.user.Authorizable; +import org.apache.jackrabbit.api.security.user.Group; +import org.apache.jackrabbit.api.security.user.Query; +import org.apache.jackrabbit.api.security.user.QueryBuilder; +import org.apache.jackrabbit.api.security.user.User; +import org.gcube.common.authorization.control.annotations.AuthorizationControl; +import org.gcube.common.gxrest.response.outbound.GXOutboundErrorResponse; +import org.gcube.common.storagehub.model.exceptions.BackendGenericError; +import org.gcube.data.access.storagehub.exception.MyAuthException; +import org.gcube.data.access.storagehub.handlers.CredentialHandler; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +@Path("groups") +public class GroupManager { + + @Context ServletContext context; + + private static final Logger log = LoggerFactory.getLogger(GroupManager.class); + + @Inject + RepositoryInitializer repository; + + @GET + @Path("") + @Produces(MediaType.APPLICATION_JSON) + @AuthorizationControl(allowed={"lucio.lelii"}, exception=MyAuthException.class) + public List getGroups(){ + + JackrabbitSession session = null; + List groups= new ArrayList<>(); + try { + session = (JackrabbitSession) repository.getRepository().login(CredentialHandler.getAdminCredentials(context)); + + Iterator result = session.getUserManager().findAuthorizables(new Query() { + + @Override + public void build(QueryBuilder builder) { + builder.setSelector(Group.class); + } + }); + + while (result.hasNext()) { + Authorizable group = result.next(); + log.info("group {} found",group.getPrincipal().getName()); + groups.add(group.getPrincipal().getName()); + } + }catch(Exception e) { + log.error("jcr error getting users", e); + GXOutboundErrorResponse.throwException(new BackendGenericError(e)); + } finally { + if (session!=null) + session.logout(); + } + return groups; + } + + @POST + @Path("") + @AuthorizationControl(allowed={"lucio.lelii"}, exception=MyAuthException.class) + public String createGroup(@FormParam("group") String group){ + + 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(); + + Group createdGroup = usrManager.createGroup(group); + groupId = createdGroup.getID(); + session.save(); + }catch(Exception e) { + log.error("jcr error creating group {}", group, e); + GXOutboundErrorResponse.throwException(new BackendGenericError(e)); + } finally { + if (session!=null) + session.logout(); + } + + return groupId; + } + + @DELETE + @Path("{id}") + @AuthorizationControl(allowed={"lucio.lelii"}, exception=MyAuthException.class) + public String deleteGroup(@PathParam("id") String id){ + + 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(); + + Authorizable authorizable = usrManager.getAuthorizable(id); + if (authorizable.isGroup()) + authorizable.remove(); + session.save(); + }catch(Exception e) { + log.error("jcr error getting users", e); + GXOutboundErrorResponse.throwException(new BackendGenericError(e)); + } finally { + if (session!=null) + session.logout(); + } + + return userId; + } + + @PUT + @Path("{id}") + @AuthorizationControl(allowed={"lucio.lelii"}, exception=MyAuthException.class) + public boolean addUserToGroup(@PathParam("id") String groupId, @FormParam("userId") String userId){ + + JackrabbitSession session = null; + boolean success = false; + try { + session = (JackrabbitSession) repository.getRepository().login(CredentialHandler.getAdminCredentials(context)); + + org.apache.jackrabbit.api.security.user.UserManager usrManager = session.getUserManager(); + + Group group = (Group)usrManager.getAuthorizable(groupId); + User user = (User)usrManager.getAuthorizable(userId); + + success = group.addMember(user); + + session.save(); + }catch(Exception e) { + log.error("jcr error adding user {} to group {}", userId, groupId, e); + GXOutboundErrorResponse.throwException(new BackendGenericError(e)); + } finally { + if (session!=null) + session.logout(); + } + + return success; + } + + @DELETE + @Path("{groupId}/users/{userId}") + @AuthorizationControl(allowed={"lucio.lelii"}, exception=MyAuthException.class) + public boolean removeUserFromGroup(@PathParam("groupId") String groupId, @PathParam("userId") String userId){ + + JackrabbitSession session = null; + boolean success = false; + try { + session = (JackrabbitSession) repository.getRepository().login(CredentialHandler.getAdminCredentials(context)); + + org.apache.jackrabbit.api.security.user.UserManager usrManager = session.getUserManager(); + + Group group = (Group)usrManager.getAuthorizable(groupId); + User user = (User)usrManager.getAuthorizable(userId); + + 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)); + } finally { + if (session!=null) + session.logout(); + } + + return success; + } + + @GET + @Path("{groupId}/users") + @AuthorizationControl(allowed={"lucio.lelii"}, exception=MyAuthException.class) + public List getUsersOfGroup(@PathParam("groupId") String groupId){ + + JackrabbitSession session = null; + List users = new ArrayList<>(); + try { + session = (JackrabbitSession) repository.getRepository().login(CredentialHandler.getAdminCredentials(context)); + + org.apache.jackrabbit.api.security.user.UserManager usrManager = session.getUserManager(); + + Group group = (Group)usrManager.getAuthorizable(groupId); + + Iterator it = group.getMembers(); + + while (it.hasNext()) { + Authorizable user = it.next(); + users.add(user.getPrincipal().getName()); + } + + + }catch(Exception e) { + log.error("jcr error getting users of group {}", groupId, e); + GXOutboundErrorResponse.throwException(new BackendGenericError(e)); + } finally { + if (session!=null) + session.logout(); + } + + return users; + } + +} diff --git a/src/main/java/org/gcube/data/access/storagehub/services/ItemSharing.java b/src/main/java/org/gcube/data/access/storagehub/services/ItemSharing.java index 38ffd68..196d5db 100644 --- a/src/main/java/org/gcube/data/access/storagehub/services/ItemSharing.java +++ b/src/main/java/org/gcube/data/access/storagehub/services/ItemSharing.java @@ -47,6 +47,7 @@ import org.gcube.data.access.storagehub.accounting.AccountingHandler; import org.gcube.data.access.storagehub.handlers.CredentialHandler; import org.gcube.data.access.storagehub.handlers.Item2NodeConverter; import org.gcube.data.access.storagehub.handlers.Node2ItemConverter; +import org.gcube.data.access.storagehub.handlers.UnshareHandler; import org.gcube.data.access.storagehub.handlers.VersionHandler; import org.gcube.smartgears.utils.InnerMethodName; import org.glassfish.jersey.media.multipart.FormDataParam; @@ -75,7 +76,7 @@ public class ItemSharing { AuthorizationChecker authChecker; @Inject - VersionHandler versionHandler; + UnshareHandler unshareHandler; @Inject Node2ItemConverter node2Item; @Inject Item2NodeConverter item2Node; @@ -185,7 +186,7 @@ public class ItemSharing { private void addUserToSharing(Node sharedFolderNode, Session ses, String user, Privilege[] userPrivileges, JackrabbitAccessControlList acls) throws RepositoryException{ ses.getWorkspace().clone(ses.getWorkspace().getName(), sharedFolderNode.getPath(), sharedFolderNode.getProperty(NodeProperty.TITLE.toString()).getString(), false); - String userRootWSId = ses.getNode(Utils.getHomePath(user).toPath()).getIdentifier(); + String userRootWSId = ses.getNode(Utils.getWorkspacePath(user).toPath()).getIdentifier(); acls.addAccessControlEntry(AccessControlUtils.getPrincipal(ses, user), userPrivileges ); Node usersNode =null; if (sharedFolderNode.hasNode(NodeConstants.USERS_NAME)) @@ -208,25 +209,8 @@ public class ItemSharing { try { ses = repository.getRepository().login(CredentialHandler.getAdminCredentials(context)); Node sharedNode = ses.getNodeByIdentifier(id); - Item item = node2Item.getItem(sharedNode, Excludes.ALL); - if (!(item instanceof FolderItem) || !((FolderItem) item).isShared() || ((SharedFolder) item).isVreFolder()) - throw new InvalidItemException("item with id "+id+" cannot be unshared"); - SharedFolder sharedItem =(SharedFolder) item; - - Set usersInSharedFolder = new HashSet<>(sharedItem.getUsers().getMap().keySet()); - usersInSharedFolder.removeAll(users); - - if (users==null || users.size()==0 || usersInSharedFolder.size()<=1) - return unshareAll(login, ses, sharedItem); - - ses.getWorkspace().getLockManager().lock(sharedNode.getPath(), true, true, 0,login); - try { - if (users.size()==1 && users.contains(login)) - toReturn = unshareCaller(login, ses, sharedItem); - else toReturn = unsharePartial(users, login, ses, sharedItem); - }finally { - ses.getWorkspace().getLockManager().unlock(sharedNode.getPath()); - } + toReturn = unshareHandler.unshare(ses, users, sharedNode, login); + if(toReturn == null ) throw new InvalidItemException("item with id "+id+" cannot be unshared"); }catch(RepositoryException re){ log.error("jcr unsharing", re); GXOutboundErrorResponse.throwException(new BackendGenericError("jcr error extracting archive", re)); @@ -244,182 +228,5 @@ public class ItemSharing { - private String unshareAll(String login, Session ses, SharedFolder item) throws StorageHubException, BackendGenericError, RepositoryException{ - - authChecker.checkAdministratorControl(ses, item); - if (!login.equals(item.getOwner())) - throw new UserNotAuthorizedException("user "+login+" not authorized to unshare all"); - - Node sharedItemNode = ses.getNodeByIdentifier(item.getId()); - - ses.getWorkspace().getLockManager().lock(sharedItemNode.getPath(), true, true, 0,login); - Node unsharedNode; - try { - log.debug("user list is empty, I'm going to remove also the shared dir"); - //TODO: take the admin folder and remove his clone then move the shared folder from share to the user home and change the folder type - String adminDirPath = (String)item.getUsers().getMap().get(login); - String[] splitString = adminDirPath.split("/"); - String parentDirectoryId = splitString[0]; - String directoryName = splitString[1]; - Node parentNode = ses.getNodeByIdentifier(parentDirectoryId); - log.debug("parent node path is {}/{}",parentNode.getPath(), directoryName); - - Node adminNode = ses.getNode(String.format("%s/%s",parentNode.getPath(), directoryName)); - adminNode.removeShare(); - - unsharedNode = createUnsharedFolder(ses, parentNode, directoryName, item.getDescription(), login); - - List itemsToCopy = Utils.getItemList(sharedItemNode, Excludes.ALL, null, true, null); - - for (Item itemCopy: itemsToCopy) { - Node itemToCopyNode = ses.getNodeByIdentifier(itemCopy.getId()); - log.debug("copying {} to {}", itemToCopyNode.getPath(), unsharedNode.getPath()); - ses.move(itemToCopyNode.getPath(), String.format("%s/%s",unsharedNode.getPath(), itemToCopyNode.getName())); - } - ses.save(); - }finally { - ses.getWorkspace().getLockManager().unlock(sharedItemNode.getPath()); - } - sharedItemNode.removeSharedSet(); - ses.save(); - log.debug("all the users have been removed, the folder is totally unshared"); - - return unsharedNode.getIdentifier(); - - } - - - - - private String unshareCaller(String login, Session ses, SharedFolder item) throws StorageHubException, RepositoryException{ - - if (login.equals(item.getOwner())) - throw new InvalidCallParameters("the callor is the owner, the folder cannot be unshared"); - - if (item.getUsers().getMap().get(login)==null) - throw new InvalidCallParameters("the folder is not shared with user "+login); - - Node sharedFolderNode =ses.getNodeByIdentifier(item.getId()); - - String parentId = removeSharingForUser(login, ses, item); - - AccessControlManager acm = ses.getAccessControlManager(); - JackrabbitAccessControlList acls = AccessControlUtils.getAccessControlList(acm, sharedFolderNode.getPath()); - - AccessControlEntry entryToDelete= null; - for (AccessControlEntry ace :acls.getAccessControlEntries()) { - if (ace.getPrincipal().getName().equals(login)) { - entryToDelete = ace; - break; - } - - } - if (entryToDelete!=null) - acls.removeAccessControlEntry(entryToDelete); - - log.debug("removed Access control entry for user {}",login); - Node sharedItemNode = ses.getNodeByIdentifier(item.getId()); - Node usersNode = sharedItemNode.getNode(NodeConstants.USERS_NAME); - usersNode.remove(); - Node newUsersNode = sharedItemNode.addNode(NodeConstants.USERS_NAME); - - item.getUsers().getMap().entrySet().stream().filter(entry -> !entry.getKey().equals(login)).forEach(entry-> {try { - newUsersNode.setProperty(entry.getKey(), (String)entry.getValue()); - } catch (Exception e) { - log.error("error adding property to shared node users node under "+item.getId()); - }}); - - acm.setPolicy(sharedFolderNode.getPath(), acls); - - ses.save(); - - return parentId; - - } - - - - - private String unsharePartial(Set usersToUnshare, String login, Session ses, SharedFolder item) throws StorageHubException, RepositoryException { - authChecker.checkAdministratorControl(ses, (SharedFolder)item); - if (usersToUnshare.contains(item.getOwner())) - throw new UserNotAuthorizedException("user "+login+" not authorized to unshare owner"); - - Node sharedFolderNode =ses.getNodeByIdentifier(item.getId()); - - AccessControlManager acm = ses.getAccessControlManager(); - JackrabbitAccessControlList acls = AccessControlUtils.getAccessControlList(acm, sharedFolderNode.getPath()); - - for (String user : usersToUnshare) { - removeSharingForUser(user, ses, item); - - AccessControlEntry entryToDelete= null; - for (AccessControlEntry ace :acls.getAccessControlEntries()) { - if (ace.getPrincipal().getName().equals(login)) { - entryToDelete = ace; - break; - } - - } - if (entryToDelete!=null) - acls.removeAccessControlEntry(entryToDelete); - } - - log.debug("removed Access control entry for user {}",login); - Node sharedItemNode = ses.getNodeByIdentifier(item.getId()); - Node usersNode = sharedItemNode.getNode(NodeConstants.USERS_NAME); - usersNode.remove(); - Node newUsersNode = sharedItemNode.addNode(NodeConstants.USERS_NAME); - - item.getUsers().getMap().entrySet().stream().filter(entry -> !usersToUnshare.contains(entry.getKey())).forEach(entry-> {try { - newUsersNode.setProperty(entry.getKey(), (String)entry.getValue()); - } catch (Exception e) { - log.error("error adding property to shared node users node under "+item.getId()); - }}); - - acm.setPolicy(sharedFolderNode.getPath(), acls); - - ses.save(); - - return item.getId(); - - } - - - public String removeSharingForUser(String user, Session ses, SharedFolder item) throws RepositoryException { - String userDirPath = (String)item.getUsers().getMap().get(user); - if (userDirPath==null) return null; - String[] splitString = userDirPath.split("/"); - String parentDirectoryId = splitString[0]; - String directoryName = splitString[1]; - 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; - } - - - private Node createUnsharedFolder(Session ses, Node destinationNode, String name, String description, String login) { - FolderItem item = new FolderItem(); - Calendar now = Calendar.getInstance(); - item.setName(name); - item.setTitle(name); - item.setDescription(description); - //item.setCreationTime(now); - item.setHidden(false); - item.setLastAction(ItemAction.CREATED); - item.setLastModificationTime(now); - item.setLastModifiedBy(login); - item.setOwner(login); - - //to inherit hidden property - //item.setHidden(destinationItem.isHidden()); - - Node newNode = item2Node.getNode(ses, destinationNode, item); - 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 d6d3ee7..5c68a6b 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 @@ -37,8 +37,13 @@ import javax.ws.rs.core.Response; import javax.ws.rs.core.StreamingOutput; import org.apache.commons.io.FilenameUtils; +import org.gcube.common.authorization.control.annotations.AuthorizationControl; import org.gcube.common.authorization.library.provider.AuthorizationProvider; +import org.gcube.common.encryption.StringEncrypter; import org.gcube.common.gxrest.response.outbound.GXOutboundErrorResponse; +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.NodeConstants; import org.gcube.common.storagehub.model.Paths; @@ -46,6 +51,7 @@ 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.items.AbstractFileItem; import org.gcube.common.storagehub.model.items.FolderItem; import org.gcube.common.storagehub.model.items.Item; @@ -62,6 +68,7 @@ import org.gcube.data.access.storagehub.Range; import org.gcube.data.access.storagehub.SingleFileStreamingOutput; import org.gcube.data.access.storagehub.Utils; import org.gcube.data.access.storagehub.accounting.AccountingHandler; +import org.gcube.data.access.storagehub.exception.MyAuthException; import org.gcube.data.access.storagehub.handlers.ClassHandler; import org.gcube.data.access.storagehub.handlers.CredentialHandler; import org.gcube.data.access.storagehub.handlers.Item2NodeConverter; @@ -71,6 +78,8 @@ import org.gcube.data.access.storagehub.handlers.VersionHandler; import org.gcube.smartgears.utils.InnerMethodName; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import static org.gcube.common.storagehub.model.Constants.*; + @Path("items") public class ItemsManager { @@ -231,6 +240,73 @@ public class ItemsManager { return new ItemList(toReturn); } + @GET + @Produces(MediaType.APPLICATION_JSON) + @Path("publiclink/{id}") + @AuthorizationControl(allowed={"URIResolver"}, exception=MyAuthException.class) + public Response resolvePublicLink() { + InnerMethodName.instance.set("resolvePubliclink"); + Session ses = null; + try{ + String login = AuthorizationProvider.instance.get().getClient().getId(); + ses = repository.getRepository().login(CredentialHandler.getAdminCredentials(context)); + + String complexId = id; + + if (id.startsWith(enchriptedPrefix)) { + String currentScope = ScopeProvider.instance.get(); + try { + ScopeBean bean= new ScopeBean(currentScope); + while (!bean.is(Type.INFRASTRUCTURE)) { + bean = bean.enclosingScope(); + } + + ScopeProvider.instance.set(bean.toString()); + complexId = StringEncrypter.getEncrypter().encrypt(id.replace(enchriptedPrefix, "")); + }catch(Exception e){ + throw new BackendGenericError("invalid public url",e); + }finally { + ScopeProvider.instance.set(currentScope); + } + + } + + String itemId = complexId; + String versionName = null; + + if (complexId.contains(versionPrefix)) { + String[] split = complexId.split(versionPrefix); + itemId = split[0]; + versionName = split[1]; + } + + Node selectedNode = ses.getNodeByIdentifier(id); + + Item item = node2Item.getItem(selectedNode, Arrays.asList(NodeConstants.ACCOUNTING_NAME, NodeConstants.METADATA_NAME)); + + if (!(item instanceof AbstractFileItem)) throw new InvalidCallParameters("the choosen item is not a File"); + + if (!(item.isPublicItem())) throw new UserNotAuthorizedException("the item requested is not public"); + + if (versionName!=null) + return downloadVersionInternal(ses, login, itemId, versionName, false); + else + return downloadFileInternal(ses, (AbstractFileItem) item, login, true); + + + }catch(RepositoryException re ){ + log.error("jcr error getting public link", re); + GXOutboundErrorResponse.throwException(new BackendGenericError(re)); + }catch(StorageHubException she ){ + log.error("error getting public link", she); + GXOutboundErrorResponse.throwException(she); + }finally{ + if (ses!=null) + ses.logout(); + } + return Response.serverError().build(); + } + @GET @Produces(MediaType.APPLICATION_JSON) @Path("{id}/publiclink") @@ -239,17 +315,15 @@ public class ItemsManager { Session ses = null; URL toReturn = null; try{ - String login = AuthorizationProvider.instance.get().getClient().getId(); ses = repository.getRepository().login(CredentialHandler.getAdminCredentials(context)); authChecker.checkReadAuthorizationControl(ses, id); Node selectedNode = ses.getNodeByIdentifier(id); - + Item item = node2Item.getItem(selectedNode, Arrays.asList(NodeConstants.ACCOUNTING_NAME, NodeConstants.METADATA_NAME)); if (!(item instanceof AbstractFileItem)) throw new InvalidCallParameters("the choosen item is not a File"); - if (version!=null) { boolean versionFound = false; VersionList versions = getVersions(); @@ -260,16 +334,38 @@ public class ItemsManager { } if (!versionFound) throw new InvalidCallParameters("the selected file has no version "+version); } - + + /* NOT SETTING THE PUBLIC ATTRIBUTE FOR SOME STRANGE REASON ses.getWorkspace().getLockManager().lock(selectedNode.getPath(), false, true, 0,login); try { selectedNode.setProperty(NodeProperty.IS_PUBLIC.toString(), true); ses.save(); }finally { ses.getWorkspace().getLockManager().unlock(selectedNode.getPath()); + }*/ + + String url = null; + String currentScope = ScopeProvider.instance.get(); + try { + ScopeBean bean= new ScopeBean(currentScope); + while (!bean.is(Type.INFRASTRUCTURE)) { + bean = bean.enclosingScope(); + } + + ScopeProvider.instance.set(bean.toString()); + + String toEnchript; + if(version!=null) toEnchript = String.format("%s%s%s",id, versionPrefix, version); + else toEnchript = id; + + String enchriptedQueryString = StringEncrypter.getEncrypter().encrypt(toEnchript); + + url = createPublicLink(enchriptedQueryString); + }catch(Exception e){ + throw new BackendGenericError(e); + }finally { + ScopeProvider.instance.set(currentScope); } - - String url = createPublicLink(version, id); toReturn = new URL(url); @@ -287,12 +383,10 @@ public class ItemsManager { return toReturn; } - - private String createPublicLink(String version, String id) { + + private String createPublicLink(String enchriptedString) { String basepath = context.getInitParameter("resolver-basepath"); - String filePublicUrl = String.format("%s/%s",basepath, id); - if (version!=null) - filePublicUrl = String.format("%s/%s", filePublicUrl, version); + String filePublicUrl = String.format("%s/%s%s",basepath, enchriptedPrefix, id); return filePublicUrl; } @@ -308,12 +402,12 @@ public class ItemsManager { authChecker.checkReadAuthorizationControl(ses, id); Node currentNode =ses.getNodeByIdentifier(id); log.trace("current node is {}",currentNode.getPath()); - + Node sharedParentNode = getSharedParentNode(currentNode); - + if (sharedParentNode==null) throw new InvalidCallParameters("item is not shared"); - + sharedParent = node2Item.getItem(sharedParentNode, excludes); }catch(RepositoryException re ){ @@ -338,7 +432,7 @@ public class ItemsManager { currentNode = currentNode.getParent(); return currentNode; } - + @GET @Path("{id}/versions") @Produces(MediaType.APPLICATION_JSON) @@ -349,15 +443,15 @@ public class ItemsManager { try{ ses = repository.getRepository().login(CredentialHandler.getAdminCredentials(context)); authChecker.checkReadAuthorizationControl(ses, id); - + Node node = ses.getNodeByIdentifier(id); - + Item currentItem = node2Item.getItem(node, Excludes.GET_ONLY_CONTENT); if (!(currentItem instanceof AbstractFileItem)) throw new InvalidItemException("this item is not versioned"); List jcrVersions = versionHandler.getContentVersionHistory(node, ses); - + for (Version version: jcrVersions) { boolean currentVersion = ((AbstractFileItem)currentItem).getContent().getStorageId().equals(version.getFrozenNode().getProperty(NodeProperty.STORAGE_ID.toString()).getString()); versions.add(new org.gcube.common.storagehub.model.service.Version(version.getIdentifier(), version.getName(), version.getCreated(), currentVersion)); @@ -377,7 +471,6 @@ public class ItemsManager { @GET @Path("{id}/versions/{version}/download") - @Produces(MediaType.APPLICATION_JSON) public Response downloadVersion(@PathParam("version") String versionName){ InnerMethodName.instance.set("downloadSpecificVersion"); Session ses = null; @@ -385,42 +478,9 @@ public class ItemsManager { String login = AuthorizationProvider.instance.get().getClient().getId(); ses = repository.getRepository().login(CredentialHandler.getAdminCredentials(context)); authChecker.checkReadAuthorizationControl(ses, id); - Node node = ses.getNodeByIdentifier(id); - Item currentItem = node2Item.getItem(node, Excludes.ALL); - if (!(currentItem instanceof AbstractFileItem)) - throw new InvalidItemException("this item is not a file"); - List jcrVersions = versionHandler.getContentVersionHistory(ses.getNodeByIdentifier(id), ses); - - for (Version version: jcrVersions) { - log.debug("retrieved version id {}, name {}", version.getIdentifier(), version.getName()); - if (version.getName().equals(versionName)) { - long size = version.getFrozenNode().getProperty(NodeProperty.SIZE.toString()).getLong(); - String mimeType = version.getFrozenNode().getProperty(NodeProperty.MIME_TYPE.toString()).getString(); - String storageId = version.getFrozenNode().getProperty(NodeProperty.STORAGE_ID.toString()).getString(); - - final InputStream streamToWrite = Utils.getStorageClient(login).getClient().get().RFileAsInputStream(storageId); - - String oldfilename = FilenameUtils.getBaseName(currentItem.getTitle()); - String ext = FilenameUtils.getExtension(currentItem.getTitle()); - - String fileName = String.format("%s_v%s.%s", oldfilename, version.getName(), ext); - - - accountingHandler.createReadObj(fileName, ses, node, true); - - StreamingOutput so = new SingleFileStreamingOutput(streamToWrite); - - return Response - .ok(so) - .header("content-disposition","attachment; filename = "+fileName) - .header("Content-Length", size) - .header("Content-Type", mimeType) - .build(); - } - } - - + return downloadVersionInternal(ses, login, id, versionName, true); + }catch(RepositoryException re ){ log.error("jcr error downloading version", re); GXOutboundErrorResponse.throwException(new BackendGenericError(re)); @@ -433,13 +493,51 @@ public class ItemsManager { } return Response.serverError().build(); } + + private Response downloadVersionInternal(Session ses, String login, String id, String versionName, boolean withAccounting) throws RepositoryException, StorageHubException{ + Node node = ses.getNodeByIdentifier(id); + Item currentItem = node2Item.getItem(node, Excludes.ALL); + if (!(currentItem instanceof AbstractFileItem)) + throw new InvalidItemException("this item is not a file"); + + List jcrVersions = versionHandler.getContentVersionHistory(ses.getNodeByIdentifier(id), ses); + + for (Version version: jcrVersions) { + log.debug("retrieved version id {}, name {}", version.getIdentifier(), version.getName()); + if (version.getName().equals(versionName)) { + long size = version.getFrozenNode().getProperty(NodeProperty.SIZE.toString()).getLong(); + String mimeType = version.getFrozenNode().getProperty(NodeProperty.MIME_TYPE.toString()).getString(); + String storageId = version.getFrozenNode().getProperty(NodeProperty.STORAGE_ID.toString()).getString(); + + final InputStream streamToWrite = Utils.getStorageClient(login).getClient().get().RFileAsInputStream(storageId); + + String oldfilename = FilenameUtils.getBaseName(currentItem.getTitle()); + String ext = FilenameUtils.getExtension(currentItem.getTitle()); + + String fileName = String.format("%s_v%s.%s", oldfilename, version.getName(), ext); + + if (withAccounting) + accountingHandler.createReadObj(fileName, ses, node, true); + + StreamingOutput so = new SingleFileStreamingOutput(streamToWrite); + + return Response + .ok(so) + .header("content-disposition","attachment; filename = "+fileName) + .header("Content-Length", size) + .header("Content-Type", mimeType) + .build(); + } + } + throw new InvalidItemException("the version is not valid"); + } @GET @Path("{id}/anchestors") @Produces(MediaType.APPLICATION_JSON) public ItemList getAnchestors(@QueryParam("exclude") List excludes){ InnerMethodName.instance.set("getAnchestors"); - org.gcube.common.storagehub.model.Path absolutePath = Utils.getHomePath(); + org.gcube.common.storagehub.model.Path absolutePath = Utils.getWorkspacePath(); Session ses = null; List toReturn = new LinkedList<>(); try{ @@ -498,21 +596,7 @@ public class ItemsManager { authChecker.checkReadAuthorizationControl(ses, id); final Item item = node2Item.getItem(node, null); if (item instanceof AbstractFileItem){ - AbstractFileItem fileItem =(AbstractFileItem) item; - - final InputStream streamToWrite = Utils.getStorageClient(login).getClient().get().RFileAsInputStream(fileItem.getContent().getStorageId()); - - accountingHandler.createReadObj(fileItem.getTitle(), ses, node, true); - - StreamingOutput so = new SingleFileStreamingOutput(streamToWrite); - - response = Response - .ok(so) - .header("content-disposition","attachment; filename = "+fileItem.getName()) - .header("Content-Length", fileItem.getContent().getSize()) - .header("Content-Type", fileItem.getContent().getMimeType()) - .build(); - + return downloadFileInternal(ses, (AbstractFileItem) item, login, true); } else if (item instanceof FolderItem){ try { @@ -559,19 +643,37 @@ public class ItemsManager { return response; } + + private Response downloadFileInternal(Session ses, AbstractFileItem fileItem, String login, boolean withAccounting) throws RepositoryException { + + final InputStream streamToWrite = Utils.getStorageClient(login).getClient().get().RFileAsInputStream(fileItem.getContent().getStorageId()); + if (withAccounting) + accountingHandler.createReadObj(fileItem.getTitle(), ses, ses.getNodeByIdentifier(fileItem.getId()), true); + + StreamingOutput so = new SingleFileStreamingOutput(streamToWrite); + + return Response + .ok(so) + .header("content-disposition","attachment; filename = "+fileItem.getName()) + .header("Content-Length", fileItem.getContent().getSize()) + .header("Content-Type", fileItem.getContent().getMimeType()) + .build(); + + } + @PUT @Path("{id}/move") public String move(@FormParam("destinationId") String destinationId){ InnerMethodName.instance.set("move"); - + Session ses = null; try{ final String login = AuthorizationProvider.instance.get().getClient().getId(); ses = repository.getRepository().login(CredentialHandler.getAdminCredentials(context)); - + authChecker.checkMoveOpsForProtectedFolders(ses, id); authChecker.checkWriteAuthorizationControl(ses, destinationId, true); authChecker.checkWriteAuthorizationControl(ses, id, false); @@ -579,36 +681,36 @@ public class ItemsManager { final Node nodeToMove = ses.getNodeByIdentifier(id); final Node destination = ses.getNodeByIdentifier(destinationId); Node originalParent = nodeToMove.getParent(); - + Item destinationItem = node2Item.getItem(destination,null); final Item item = node2Item.getItem(nodeToMove, null); if (item instanceof SharedFolder) throw new InvalidItemException("shared folder cannot be moved"); - + if (item instanceof FolderItem && Utils.hasSharedChildren(nodeToMove)) throw new InvalidItemException("folder item with shared children cannot be moved"); - + if (Constants.FOLDERS_TO_EXLUDE.contains(item.getTitle()) || Constants.FOLDERS_TO_EXLUDE.contains(destinationItem.getTitle())) throw new InvalidItemException("protected folder cannot be moved"); if (!(destinationItem instanceof FolderItem)) throw new InvalidItemException("destination item is not a folder"); - + if (item.isShared() && (!destinationItem.isShared() || !getSharedParentNode(nodeToMove).getIdentifier().equals(getSharedParentNode(destination).getIdentifier()))) throw new InvalidCallParameters("shared Item cannot be moved in a different shared folder or in a private folder"); - + ses.getWorkspace().getLockManager().lock(destination.getPath(), false, true, 0,login); ses.getWorkspace().getLockManager().lock(nodeToMove.getPath(), true, true, 0,login); try { String uniqueName =(Utils.checkExistanceAndGetUniqueName(ses, destination, nodeToMove.getName())); String newPath = String.format("%s/%s",destination.getPath(), uniqueName); - + ses.getWorkspace().move(nodeToMove.getPath(), newPath); Utils.setPropertyOnChangeNode(ses.getNode(newPath), login, ItemAction.MOVED); - + String mimeTypeForAccounting = (item instanceof AbstractFileItem)? ((AbstractFileItem) item).getContent().getMimeType(): null; - + accountingHandler.createFolderAddObj(uniqueName, item.getClass().getSimpleName(), mimeTypeForAccounting , ses, destination, false); accountingHandler.createFolderRemoveObj(item.getTitle(), item.getClass().getSimpleName(), mimeTypeForAccounting, ses, originalParent, false); ses.save(); @@ -616,7 +718,7 @@ public class ItemsManager { ses.getWorkspace().getLockManager().unlock(nodeToMove.getPath()); ses.getWorkspace().getLockManager().unlock(destination.getPath()); } - + }catch(RepositoryException re ){ log.error("jcr error moving item", re); GXOutboundErrorResponse.throwException(new BackendGenericError(re)); @@ -665,7 +767,7 @@ public class ItemsManager { ses.getWorkspace().copy(nodeToCopy.getPath(), newPath); Node newNode = ses.getNode(newPath); newFileIdentifier = newNode.getIdentifier(); - + if (item instanceof AbstractFileItem) { String oldStorageId = ((AbstractFileItem)item).getContent().getStorageId(); String newStorageID = Utils.getStorageClient(login).getClient().copyFile(true).from(oldStorageId).to(newPath); @@ -674,15 +776,15 @@ public class ItemsManager { ((AbstractFileItem) item).getContent().setRemotePath(newPath); item2Node.replaceContent(ses, newNode, (AbstractFileItem) item, ItemAction.CLONED); } - + Utils.setPropertyOnChangeNode(newNode, login, ItemAction.CLONED); newNode.setProperty(NodeProperty.PORTAL_LOGIN.toString(), login); newNode.setProperty(NodeProperty.IS_PUBLIC.toString(), false); - + String mimeTypeForAccounting = (item instanceof AbstractFileItem)? ((AbstractFileItem) item).getContent().getMimeType(): null; accountingHandler.createFolderAddObj(uniqueName, item.getClass().getSimpleName(), mimeTypeForAccounting, ses, destination, false); - - + + ses.save(); }finally { @@ -714,7 +816,7 @@ public class ItemsManager { final String login = AuthorizationProvider.instance.get().getClient().getId(); ses = repository.getRepository().login(CredentialHandler.getAdminCredentials(context)); - + authChecker.checkMoveOpsForProtectedFolders(ses, id); authChecker.checkWriteAuthorizationControl(ses, id, false); @@ -744,7 +846,7 @@ public class ItemsManager { ses.getWorkspace().getLockManager().unlock(nodeToMove.getPath()); ses.getWorkspace().getLockManager().unlock(nodeToMove.getParent().getPath()); } - + }catch(RepositoryException re ){ log.error("jcr error moving item", re); GXOutboundErrorResponse.throwException(new BackendGenericError(re)); @@ -777,7 +879,7 @@ public class ItemsManager { final Node nodeToUpdate = ses.getNodeByIdentifier(id); - + ses.getWorkspace().getLockManager().lock(nodeToUpdate.getPath(), false, true, 0,login); try { item2Node.updateMetadataNode(ses, nodeToUpdate, metadata.getMap(), login); @@ -786,7 +888,7 @@ public class ItemsManager { ses.getWorkspace().getLockManager().unlock(nodeToUpdate.getPath()); } //TODO: UPDATE accounting - + }catch(RepositoryException re ){ log.error("jcr error moving item", re); GXOutboundErrorResponse.throwException(new BackendGenericError(re)); @@ -802,13 +904,13 @@ public class ItemsManager { return Response.ok(id).build(); } - + @DELETE @Path("{id}") public Response deleteItem(){ InnerMethodName.instance.set("deleteItem"); - + //TODO: check also that is not already trashed Session ses = null; try{ @@ -848,5 +950,5 @@ public class ItemsManager { return Response.ok().build(); } - + } \ No newline at end of file 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 ded6afc..59c902b 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 @@ -1,12 +1,14 @@ package org.gcube.data.access.storagehub.services; import java.util.ArrayList; +import java.util.Collections; import java.util.Iterator; import java.util.List; import javax.inject.Inject; import javax.jcr.Node; -import javax.jcr.Session; +import javax.jcr.NodeIterator; +import javax.jcr.query.QueryResult; import javax.servlet.ServletContext; import javax.ws.rs.DELETE; import javax.ws.rs.FormParam; @@ -23,13 +25,16 @@ import org.apache.jackrabbit.api.security.user.Authorizable; import org.apache.jackrabbit.api.security.user.Query; import org.apache.jackrabbit.api.security.user.QueryBuilder; import org.apache.jackrabbit.api.security.user.User; -import org.gcube.common.authorization.library.annotations.AuthorizationControl; -import org.gcube.common.authorization.library.provider.AuthorizationProvider; +import org.apache.jackrabbit.core.security.principal.PrincipalImpl; +import org.gcube.common.authorization.control.annotations.AuthorizationControl; import org.gcube.common.gxrest.response.outbound.GXOutboundErrorResponse; import org.gcube.common.storagehub.model.exceptions.BackendGenericError; +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.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -43,10 +48,13 @@ public class UserManager { @Inject RepositoryInitializer repository; + @Inject + UnshareHandler unshareHandler; + @GET @Path("") @Produces(MediaType.APPLICATION_JSON) - @AuthorizationControl(allowed={"lucio.lelii"}) + @AuthorizationControl(allowed={"lucio.lelii"}, exception=MyAuthException.class) public List getUsers(){ JackrabbitSession session = null; @@ -64,7 +72,7 @@ public class UserManager { while (result.hasNext()) { Authorizable user = result.next(); - log.info("user {} found",user.getPrincipal().getName()); + log.debug("user {} found",user.getPrincipal().getName()); users.add(user.getPrincipal().getName()); } }catch(Exception e) { @@ -79,7 +87,7 @@ public class UserManager { @POST @Path("") - @AuthorizationControl(allowed={"lucio.lelii"}) + @AuthorizationControl(allowed={"lucio.lelii"}, exception=MyAuthException.class) public String createUser(@FormParam("user") String user, @FormParam("password") String password){ JackrabbitSession session = null; @@ -95,13 +103,16 @@ public class UserManager { Node homeNode = session.getNode("/Home"); Node userHome = homeNode.addNode(user, "nthl:home"); + //creating workspace folder Node workspaceFolder = Utils.createFolderInternally(session, userHome, Constants.WORKSPACE_ROOT_FOLDER_NAME, "workspace of "+user, false, user, null); - Node trashFolder = Utils.createFolderInternally(session, workspaceFolder, Constants.TRASH_ROOT_FOLDER_NAME, "trash of "+user, false, user, null); - Node specialFolder = Utils.createFolderInternally(session, workspaceFolder, Constants.VRE_FOLDER_PARENT_NAME, "special folder container of "+user, false, user, null); + //creating thrash folder + Utils.createFolderInternally(session, workspaceFolder, Constants.TRASH_ROOT_FOLDER_NAME, "trash of "+user, false, user, null); + //creating Vre container folder + 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 getting users", e); + log.error("jcr error creating user {}", user, e); GXOutboundErrorResponse.throwException(new BackendGenericError(e)); } finally { if (session!=null) @@ -113,7 +124,7 @@ public class UserManager { @DELETE @Path("{id}") - @AuthorizationControl(allowed={"lucio.lelii"}) + @AuthorizationControl(allowed={"lucio.lelii"}, exception=MyAuthException.class) public String deleteUser(@PathParam("id") String id){ JackrabbitSession session = null; @@ -122,10 +133,31 @@ public class UserManager { 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); + + + String sql2Query = String.format("SELECT * FROM [nthl:workspaceSharedItem] AS node WHERE ISDESCENDANTNODE('%s')", path.toPath()); - Authorizable authorizable = usrManager.getAuthorizable(id); - if (!authorizable.isGroup()) + log.info("query sent is {}",sql2Query); + + + javax.jcr.query.Query jcrQuery = session.getWorkspace().getQueryManager().createQuery(sql2Query, Constants.QUERY_LANGUAGE); + + QueryResult result = jcrQuery.execute(); + NodeIterator nodeIt = result.getNodes(); + while (nodeIt.hasNext()) { + 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); + } + + Authorizable authorizable = usrManager.getAuthorizable(new PrincipalImpl(id)); + if (!authorizable.isGroup()) { + log.info("removing user {}", id); authorizable.remove(); + } session.save(); }catch(Exception e) { log.error("jcr error getting users", e); 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 add5e88..05ffbba 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 @@ -98,8 +98,8 @@ public class WorkspaceManager { Session ses = null; org.gcube.common.storagehub.model.Path absolutePath; if (relPath==null) - absolutePath = Utils.getHomePath(); - else absolutePath = Paths.append(Utils.getHomePath(), relPath); + absolutePath = Utils.getWorkspacePath(); + else absolutePath = Paths.append(Utils.getWorkspacePath(), relPath); Item toReturn = null; try{ @@ -124,7 +124,7 @@ public class WorkspaceManager { } private synchronized VRE getVreFolderItem(Session ses) throws RepositoryException, BackendGenericError{ - org.gcube.common.storagehub.model.Path vrePath = Paths.append(Utils.getHomePath(), Constants.VRE_FOLDER_PARENT_NAME); + 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("/", "-"); @@ -208,7 +208,7 @@ public class WorkspaceManager { InnerMethodName.instance.set("getTrashRootFolder"); Session ses = null; - org.gcube.common.storagehub.model.Path trashPath = Paths.append(Utils.getHomePath(), Constants.TRASH_ROOT_FOLDER_NAME); + org.gcube.common.storagehub.model.Path trashPath = Paths.append(Utils.getWorkspacePath(), Constants.TRASH_ROOT_FOLDER_NAME); Item item = null; try{ long start = System.currentTimeMillis(); @@ -237,7 +237,7 @@ public class WorkspaceManager { public String emptyTrash(){ InnerMethodName.instance.set("emptyTrash"); Session ses = null; - org.gcube.common.storagehub.model.Path trashPath = Paths.append(Utils.getHomePath(), Constants.TRASH_ROOT_FOLDER_NAME); + org.gcube.common.storagehub.model.Path trashPath = Paths.append(Utils.getWorkspacePath(), Constants.TRASH_ROOT_FOLDER_NAME); String toReturn = null; try{ ses = repository.getRepository().login(CredentialHandler.getAdminCredentials(context)); @@ -307,7 +307,7 @@ public class WorkspaceManager { InnerMethodName.instance.set("getVreFolders"); Session ses = null; - org.gcube.common.storagehub.model.Path vrePath = Paths.append(Utils.getHomePath(), Constants.VRE_FOLDER_PARENT_NAME); + org.gcube.common.storagehub.model.Path vrePath = Paths.append(Utils.getWorkspacePath(), Constants.VRE_FOLDER_PARENT_NAME); List toReturn = null; try{ ses = repository.getRepository().login(CredentialHandler.getAdminCredentials(context)); @@ -332,7 +332,7 @@ public class WorkspaceManager { public ItemList getVreFoldersPaged(@QueryParam("start") Integer start, @QueryParam("limit") Integer limit){ InnerMethodName.instance.set("getVreFoldersPaged"); Session ses = null; - org.gcube.common.storagehub.model.Path vrePath = Paths.append(Utils.getHomePath(), Constants.VRE_FOLDER_PARENT_NAME); + org.gcube.common.storagehub.model.Path vrePath = Paths.append(Utils.getWorkspacePath(), Constants.VRE_FOLDER_PARENT_NAME); List toReturn = null; try{ ses = repository.getRepository().login(CredentialHandler.getAdminCredentials(context)); @@ -365,7 +365,7 @@ public class WorkspaceManager { ObjectMapper mapper = new ObjectMapper(); Expression expression = mapper.readValue(jsonExpr, Expression.class); - String stringExpression = evaluator.evaluate(new And(new ISDescendant(Utils.getHomePath()), expression)); + String stringExpression = evaluator.evaluate(new And(new ISDescendant(Utils.getWorkspacePath()), expression)); String orderBy = ""; if (orderField!=null && orderField.size()>0) @@ -376,7 +376,7 @@ public class WorkspaceManager { log.info("query sent is {}",sql2Query); - String login = AuthorizationProvider.instance.get().getClient().getId(); + ses = repository.getRepository().login(CredentialHandler.getAdminCredentials(context)); Query jcrQuery = ses.getWorkspace().getQueryManager().createQuery(sql2Query, Constants.QUERY_LANGUAGE); diff --git a/src/main/webapp/WEB-INF/gcube-app.xml b/src/main/webapp/WEB-INF/gcube-app.xml index 4f0d8e1..a782a5e 100644 --- a/src/main/webapp/WEB-INF/gcube-app.xml +++ b/src/main/webapp/WEB-INF/gcube-app.xml @@ -3,5 +3,12 @@ DataAccess 1.0.0-SNAPSHOT Storage Hub webapp + + + + \ No newline at end of file