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.NodeIterator; import javax.jcr.RepositoryException; import javax.jcr.Session; import javax.jcr.lock.LockException; 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.ItemLockedException; 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.common.storagehub.model.types.NodeProperty; import org.gcube.data.access.storagehub.AuthorizationChecker; import org.gcube.data.access.storagehub.PathUtil; import org.gcube.data.access.storagehub.Utils; import org.gcube.data.access.storagehub.accounting.AccountingHandler; import org.gcube.data.access.storagehub.handlers.items.Item2NodeConverter; import org.gcube.data.access.storagehub.handlers.items.Node2ItemConverter; 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 PathUtil pathUtil; @Inject Item2NodeConverter item2Node; public String unshare(Session ses, Set users, Node sharedNode, String login) throws RepositoryException, StorageHubException{ return _unshare(ses, users, sharedNode, login, true); } public String unshareForRemoval(Session ses, Set users, Node sharedNode, String login) throws RepositoryException, StorageHubException{ return _unshare(ses, users, sharedNode, login, false); } public String _unshare(Session ses, Set users, Node sharedNode, String login, boolean withCopyOnUnshare) throws RepositoryException, StorageHubException{ Item item = node2Item.getItem(sharedNode, Excludes.ALL); if (!(item instanceof FolderItem) || !((FolderItem) item).isShared() || ((SharedFolder) item).isVreFolder()) { log.warn("this item type cannot be unshared {}",item.getClass().getSimpleName()); return null; } SharedFolder sharedItem =(SharedFolder) item; Set usersInSharedFolder = new HashSet<>(sharedItem.getUsers().getMap().keySet()); usersInSharedFolder.removeAll(users); if (users==null || users.size()==0) return unshareAll(login, ses, sharedItem, withCopyOnUnshare); if (usersInSharedFolder.size()<=1) { if (users.size()==1 && users.contains(login)) return unshareAll(sharedItem.getOwner(), ses , sharedItem, withCopyOnUnshare); else return unshareAll(login, ses, sharedItem, withCopyOnUnshare); } try { ses.getWorkspace().getLockManager().lock(sharedNode.getPath(), true, true, 0,login); }catch (LockException e) { throw new ItemLockedException(e); } 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, boolean withCopyCreation) throws StorageHubException, BackendGenericError, RepositoryException{ log.info("unshare all called"); if (!login.equals(item.getOwner())) throw new UserNotAuthorizedException("user "+login+" not authorized to unshare all"); Node sharedItemNode = ses.getNodeByIdentifier(item.getId()); try { ses.getWorkspace().getLockManager().lock(sharedItemNode.getPath(), true, true, 0,login); }catch (LockException e) { throw new ItemLockedException(e); } String unsharedNodeIdentifier =null; try { log.debug("user list is empty, I'm going to remove also the shared dir"); if (withCopyCreation) { //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(); */ Node sharedOwnerNode = null; NodeIterator it = sharedItemNode.getSharedSet(); while(it.hasNext()) { Node node = it.nextNode(); log.info("[UNSHARE] checking node {} starts with {} ",node.getPath(),pathUtil.getHome(login).toPath()); if (node.getPath().startsWith(pathUtil.getHome(login).toPath())) { sharedOwnerNode =node; break; } } Node shareParent = sharedOwnerNode.getParent(); sharedOwnerNode.removeShare(); Node unsharedNode = createUnsharedFolder(ses, shareParent , item.getTitle() , 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())); } unsharedNode.getNode(NodeProperty.ACCOUNTING.toString()).remove(); ses.move(sharedItemNode.getNode(NodeProperty.ACCOUNTING.toString()).getPath(), String.format("%s/%s",unsharedNode.getPath(), NodeProperty.ACCOUNTING.toString())); //set owner of all the unshared items to the caller item2Node.updateOwnerOnSubTree(unsharedNode, login); accountingHandler.createUnshareFolder(sharedItemNode.getProperty(NodeProperty.TITLE.toString()).getString(), ses, "ALL", unsharedNode, false); unsharedNodeIdentifier = unsharedNode.getIdentifier(); log.info("[UNSHARE] unshared node id {}",unsharedNodeIdentifier); ses.save(); } log.debug("all the users have been removed, the folder is totally unshared"); }catch(Throwable t) { log.error("erro unsharing all",t); throw t; }finally { ses.getWorkspace().getLockManager().unlock(sharedItemNode.getPath()); } sharedItemNode.removeSharedSet(); ses.save(); return unsharedNodeIdentifier; } private String unshareCaller(String login, Session ses, SharedFolder item) throws StorageHubException, RepositoryException{ log.info("unshare caller"); if (login.equals(item.getOwner())) throw new InvalidCallParameters("the caller is the owner, the folder cannot be unshared"); Node sharedFolderNode =ses.getNodeByIdentifier(item.getId()); if (item.getUsers().getMap().get(login)!=null) { Node usersNode = sharedFolderNode.getNode(NodeConstants.USERS_NAME); usersNode.remove(); Node newUsersNode = sharedFolderNode.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()); }}); } Node shareNode = getUserSharingNode(login, ses, item); String parentId = shareNode.getParent().getIdentifier(); //not returning an error to correct all the old ACL if (shareNode != null) shareNode.removeShare(); 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); acm.setPolicy(sharedFolderNode.getPath(), acls); log.debug("removed Access control entry for user {}",login); accountingHandler.createUnshareFolder(item.getTitle(), ses, login, sharedFolderNode, false); ses.save(); return parentId; } private String unsharePartial(Set usersToUnshare, String login, Session ses, SharedFolder item) throws StorageHubException, RepositoryException { log.info("unshare partial"); 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) { Node userShareNode = getUserSharingNode(user, ses, item); //not returning an error to correct all the old ACL if (userShareNode != null) userShareNode.removeShare(); AccessControlEntry entryToDelete= null; for (AccessControlEntry ace :acls.getAccessControlEntries()) { if (ace.getPrincipal().getName().equals(user)) { entryToDelete = ace; break; } } if (entryToDelete!=null) acls.removeAccessControlEntry(entryToDelete); log.debug("removed Access control entry for user {}",user); } 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); for (String user: usersToUnshare) { accountingHandler.createUnshareFolder(sharedItemNode.getProperty(NodeProperty.TITLE.toString()).getString(), ses, user, sharedItemNode, false); } ses.save(); return item.getId(); } private Node getUserSharingNode(String user, Session ses, SharedFolder item) throws RepositoryException { Node shareNode = null; try { 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 = null; parentNode = ses.getNodeByIdentifier(parentDirectoryId); shareNode = ses.getNode(String.format("%s/%s",parentNode.getPath(), directoryName)); }catch (Throwable e) { log.warn("users map is not containing a valid value"); } Node sharedFolderNode = ses.getNodeByIdentifier(item.getId()); if (shareNode==null) { NodeIterator it = sharedFolderNode.getSharedSet(); while(it.hasNext()) { Node node = it.nextNode(); if (node.getPath().startsWith(pathUtil.getHome(user).toPath())) { shareNode =node; break; } } } return shareNode; } 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(destinationNode, item); return newNode; } }