You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
storagehub/src/main/java/org/gcube/data/access/storagehub/services/ItemSharing.java

359 lines
13 KiB
Java

package org.gcube.data.access.storagehub.services;
import java.util.HashMap;
import java.util.Map.Entry;
import java.util.Set;
import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;
import javax.jcr.Node;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.lock.LockException;
import javax.jcr.security.AccessControlManager;
import javax.jcr.security.Privilege;
import javax.servlet.ServletContext;
import javax.ws.rs.Consumes;
import javax.ws.rs.FormParam;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import org.apache.jackrabbit.api.security.JackrabbitAccessControlList;
import org.apache.jackrabbit.commons.jackrabbit.authorization.AccessControlUtils;
import org.gcube.com.fasterxml.jackson.databind.ObjectMapper;
import org.gcube.common.gxrest.response.outbound.GXOutboundErrorResponse;
import org.gcube.common.storagehub.model.Excludes;
import org.gcube.common.storagehub.model.NodeConstants;
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.ItemLockedException;
import org.gcube.common.storagehub.model.exceptions.StorageHubException;
import org.gcube.common.storagehub.model.items.ExternalFolder;
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.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.PathUtil;
import org.gcube.data.access.storagehub.StorageHubAppllicationManager;
import org.gcube.data.access.storagehub.Utils;
import org.gcube.data.access.storagehub.accounting.AccountingHandler;
import org.gcube.data.access.storagehub.handlers.CredentialHandler;
import org.gcube.data.access.storagehub.handlers.UnshareHandler;
import org.gcube.data.access.storagehub.handlers.items.Item2NodeConverter;
import org.gcube.data.access.storagehub.handlers.items.Node2ItemConverter;
import org.gcube.smartgears.utils.InnerMethodName;
import org.glassfish.jersey.media.multipart.FormDataParam;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Path("items")
public class ItemSharing extends Impersonable{
private static final Logger log = LoggerFactory.getLogger(ItemSharing.class);
RepositoryInitializer repository = StorageHubAppllicationManager.repository;
@Inject
AccountingHandler accountingHandler;
@RequestScoped
@PathParam("id")
String id;
@Context
ServletContext context;
@Inject
AuthorizationChecker authChecker;
@Inject
PathUtil pathUtil;
@Inject
UnshareHandler unshareHandler;
@Inject Node2ItemConverter node2Item;
@Inject Item2NodeConverter item2Node;
@SuppressWarnings("unchecked")
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
@POST
@Path("{id}/share")
public String shareWithMap(@FormParam("mapUserPermission") String mapUserPermissionString, @FormParam("defaultAccessType") String defaultAccessTypeString){
InnerMethodName.instance.set("shareFolder");
HashMap<String,String> mapUserPermission;
Session ses = null;
String toReturn = null;
try{
try {
mapUserPermission = (HashMap<String,String>)new ObjectMapper().readValue(mapUserPermissionString, HashMap.class);
}catch (Exception e) {
throw new InvalidCallParameters("invalid map passed");
}
AccessType defaultAccessType;
try {
defaultAccessType = AccessType.fromValue(defaultAccessTypeString);
}catch (IllegalArgumentException e) {
throw new InvalidCallParameters("invalid default accessType");
}
ses = repository.getRepository().login(CredentialHandler.getAdminCredentials(context));
authChecker.checkWriteAuthorizationControl(ses, currentUser, id, false);
Item item = node2Item.getItem(ses.getNodeByIdentifier(id), Excludes.ALL);
if (mapUserPermission==null || mapUserPermission.isEmpty())
throw new InvalidCallParameters("users is empty");
Node nodeToShare = ses.getNodeByIdentifier(id);
boolean alreadyShared = false;
Node sharedFolderNode;
if (!node2Item.checkNodeType(nodeToShare, SharedFolder.class))
sharedFolderNode= shareFolder(nodeToShare, ses);
else {
sharedFolderNode = nodeToShare;
alreadyShared = true;
}
ses.save();
try {
ses.getWorkspace().getLockManager().lock(sharedFolderNode.getPath(), true, true, 0,currentUser);
}catch (LockException e) {
throw new ItemLockedException(e);
}
try {
AccessControlManager acm = ses.getAccessControlManager();
JackrabbitAccessControlList acls = AccessControlUtils.getAccessControlList(acm, sharedFolderNode.getPath());
if (!alreadyShared) {
Privilege[] adminPrivileges = new Privilege[] { acm.privilegeFromName(AccessType.ADMINISTRATOR.getValue()) };
addUserToSharing(sharedFolderNode, ses, currentUser, item, adminPrivileges, acls);
mapUserPermission.remove(currentUser);
}
for (Entry<String, String> entry: mapUserPermission.entrySet() )
try {
AccessType accessType = defaultAccessType;
try {
if (entry.getValue()!=null)
accessType = AccessType.fromValue(entry.getValue());
}catch (IllegalArgumentException e) { log.warn("an illegal accessType passed to sharing ({})", entry.getValue());}
Privilege[] userPrivileges = new Privilege[] { acm.privilegeFromName(accessType.getValue()) };
addUserToSharing(sharedFolderNode, ses, entry.getKey(), null, userPrivileges, acls);
}catch(Exception e){
log.warn("error adding user {} to sharing of folder {}", entry.getKey(), sharedFolderNode.getName());
}
acm.setPolicy(sharedFolderNode.getPath(), acls);
String title = sharedFolderNode.getProperty(NodeProperty.TITLE.toString()).getString();
accountingHandler.createShareFolder(title, mapUserPermission.keySet(), ses, sharedFolderNode, currentUser, false);
ses.save();
toReturn = sharedFolderNode.getIdentifier();
} finally {
if (!ses.hasPendingChanges())
ses.getWorkspace().getLockManager().unlock(sharedFolderNode.getPath());
}
}catch(RepositoryException re){
log.error("jcr sharing", re);
GXOutboundErrorResponse.throwException(new BackendGenericError("jcr error sharing folder", re));
}catch(StorageHubException she ){
log.error(she.getErrorMessage(), she);
GXOutboundErrorResponse.throwException(she, Response.Status.fromStatusCode(she.getStatus()));
}finally{
if (ses!=null)
ses.logout();
}
return toReturn;
}
@PUT
@Path("{id}/share")
@Consumes(MediaType.MULTIPART_FORM_DATA)
@Deprecated
public String share(@FormDataParam("users") Set<String> users, @FormDataParam("defaultAccessType") AccessType accessType){
InnerMethodName.instance.set("shareFolder");
Session ses = null;
String toReturn = null;
try{
ses = repository.getRepository().login(CredentialHandler.getAdminCredentials(context));
authChecker.checkWriteAuthorizationControl(ses, currentUser, id, false);
Item item = node2Item.getItem(ses.getNodeByIdentifier(id), Excludes.ALL);
if (accessType==null)
accessType = AccessType.READ_ONLY;
if (users==null || users.isEmpty())
throw new InvalidCallParameters("users is empty");
Node nodeToShare = ses.getNodeByIdentifier(id);
boolean alreadyShared = false;
Node sharedFolderNode;
if (!node2Item.checkNodeType(nodeToShare, SharedFolder.class))
sharedFolderNode= shareFolder(nodeToShare, ses);
else {
sharedFolderNode = nodeToShare;
alreadyShared = true;
}
ses.save();
try {
ses.getWorkspace().getLockManager().lock(sharedFolderNode.getPath(), true, true, 0,currentUser);
}catch (LockException e) {
throw new ItemLockedException(e);
}
try {
AccessControlManager acm = ses.getAccessControlManager();
JackrabbitAccessControlList acls = AccessControlUtils.getAccessControlList(acm, sharedFolderNode.getPath());
if (!alreadyShared) {
Privilege[] adminPrivileges = new Privilege[] { acm.privilegeFromName(AccessType.ADMINISTRATOR.getValue()) };
addUserToSharing(sharedFolderNode, ses, currentUser, item, adminPrivileges, acls);
users.remove(currentUser);
}
Privilege[] userPrivileges = new Privilege[] { acm.privilegeFromName(accessType.getValue()) };
for (String user : users)
try {
addUserToSharing(sharedFolderNode, ses, user, null, userPrivileges, acls);
}catch(Exception e){
log.warn("error adding user {} to sharing of folder {}", user, sharedFolderNode.getName());
}
acm.setPolicy(sharedFolderNode.getPath(), acls);
accountingHandler.createShareFolder(sharedFolderNode.getProperty(NodeProperty.TITLE.toString()).getString(), users, ses, sharedFolderNode, currentUser, false);
ses.save();
toReturn = sharedFolderNode.getIdentifier();
} finally {
if (!ses.hasPendingChanges())
ses.getWorkspace().getLockManager().unlock(sharedFolderNode.getPath());
}
}catch(RepositoryException re){
log.error("jcr sharing", re);
GXOutboundErrorResponse.throwException(new BackendGenericError("jcr error sharing folder", re));
}catch(StorageHubException she ){
log.error(she.getErrorMessage(), she);
GXOutboundErrorResponse.throwException(she, Response.Status.fromStatusCode(she.getStatus()));
}finally{
if (ses!=null)
ses.logout();
}
return toReturn;
}
private Node shareFolder(Node node, Session ses) throws RepositoryException, BackendGenericError, StorageHubException{
if (!node2Item.checkNodeType(node, FolderItem.class) || Utils.hasSharedChildren(node) || !node.getProperty(NodeProperty.PORTAL_LOGIN.toString()).getString().equals(currentUser))
throw new InvalidItemException("item with id "+id+" cannot be shared");
String sharedFolderName = node.getIdentifier();
String newNodePath = Constants.SHARED_FOLDER_PATH+"/"+sharedFolderName;
ses.move(node.getPath(),newNodePath);
Node sharedFolderNode = ses.getNode(newNodePath);
sharedFolderNode.setPrimaryType(PrimaryNodeType.NT_WORKSPACE_SHARED_FOLDER);
return sharedFolderNode;
}
private void addUserToSharing(Node sharedFolderNode, Session ses, String user, Item itemToShare, Privilege[] userPrivileges, JackrabbitAccessControlList acls) throws RepositoryException{
String userRootWSId;
String userPath;
if (itemToShare==null) {
String userRootWS = pathUtil.getWorkspacePath(user).toPath();
userRootWSId = ses.getNode(userRootWS).getIdentifier();
userPath = String.format("%s%s",userRootWS,sharedFolderNode.getProperty(NodeProperty.TITLE.toString()).getString());
}
else {
userPath = itemToShare.getPath();
userRootWSId = itemToShare.getParentId();
}
log.info("cloning directory to {} ",userPath);
ses.getWorkspace().clone(ses.getWorkspace().getName(), sharedFolderNode.getPath(), userPath , false);
acls.addAccessControlEntry(AccessControlUtils.getPrincipal(ses, user), userPrivileges );
Node usersNode =null;
if (sharedFolderNode.hasNode(NodeConstants.USERS_NAME))
usersNode = sharedFolderNode.getNode(NodeConstants.USERS_NAME);
else
usersNode = sharedFolderNode.addNode(NodeConstants.USERS_NAME);
usersNode.setProperty(user, String.format("%s/%s",userRootWSId,sharedFolderNode.getProperty(NodeProperty.TITLE.toString()).getString()));
}
@PUT
@Path("{id}/unshare")
@Consumes(MediaType.MULTIPART_FORM_DATA)
public String unshare(@FormDataParam("users") Set<String> users){
InnerMethodName.instance.set("unshareFolder");
Session ses = null;
String toReturn = null;
try {
log.debug("unsharing folder with id {} with users {}", id, users);
ses = repository.getRepository().login(CredentialHandler.getAdminCredentials(context));
Node sharedNode = ses.getNodeByIdentifier(id);
toReturn = unshareHandler.unshare(ses, users, sharedNode, currentUser);
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));
}catch(StorageHubException she ){
log.error(she.getErrorMessage(), she);
GXOutboundErrorResponse.throwException(she, Response.Status.fromStatusCode(she.getStatus()));
}finally{
if (ses!=null)
ses.logout();
}
return toReturn;
}
}