package org.gcube.data.access.storagehub.services; import java.io.InputStream; import java.io.OutputStream; import java.net.URL; import java.util.Arrays; import java.util.Deque; import java.util.List; import java.util.zip.Deflater; import java.util.zip.ZipOutputStream; import javax.enterprise.context.RequestScoped; import javax.inject.Inject; import javax.jcr.Node; import javax.jcr.Session; import javax.jcr.SimpleCredentials; import javax.servlet.ServletContext; import javax.ws.rs.GET; 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.WebApplicationException; import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import javax.ws.rs.core.StreamingOutput; import org.gcube.common.authorization.library.provider.AuthorizationProvider; import org.gcube.common.storagehub.model.Paths; import org.gcube.common.storagehub.model.items.AbstractFileItem; 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.service.ItemList; import org.gcube.common.storagehub.model.service.ItemWrapper; import org.gcube.data.access.storagehub.AuthorizationChecker; import org.gcube.data.access.storagehub.Constants; 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.handlers.ItemHandler; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @Path("item") public class ItemsManager { private static final Logger log = LoggerFactory.getLogger(ItemsManager.class); @Inject RepositoryInitializer repository; @Inject AccountingHandler accountingHandler; @RequestScoped @PathParam("id") String id; @Context ServletContext context; @Inject AuthorizationChecker authChecker; @GET() @Path("{id}") @Produces(MediaType.APPLICATION_JSON) public ItemWrapper getById(@QueryParam("exclude") List excludes){ Session ses = null; Item toReturn = null; try{ String login = AuthorizationProvider.instance.get().getClient().getId(); long start = System.currentTimeMillis(); ses = repository.getRepository().login(new SimpleCredentials(login,Utils.getSecurePassword(login).toCharArray())); authChecker.checkReadAuthorizationControl(ses, id); log.info("time to connect to repo {}",(System.currentTimeMillis()-start)); log.info("excludes is {}",excludes); toReturn = ItemHandler.getItem(ses.getNodeByIdentifier(id), excludes); }catch(Throwable e){ log.error("error reading the node children of {}",id,e); throw new WebApplicationException(e); }finally{ if (ses!=null) ses.logout(); } return new ItemWrapper(toReturn); } @GET @Path("{id}/children/count") @Produces(MediaType.APPLICATION_JSON) public Long countById(@QueryParam("showHidden") Boolean showHidden, @QueryParam("exclude") List excludes){ Session ses = null; Long toReturn = null; try{ String login = AuthorizationProvider.instance.get().getClient().getId(); long start = System.currentTimeMillis(); ses = repository.getRepository().login(new SimpleCredentials(login,Utils.getSecurePassword(login).toCharArray())); authChecker.checkReadAuthorizationControl(ses, id); log.info("time to connect to repo {}",(System.currentTimeMillis()-start)); log.info("excludes is {}",excludes); toReturn = Utils.getItemCount(ses.getNodeByIdentifier(id), showHidden==null?false:showHidden); }catch(Throwable e){ log.error("error reading the node children of {}",id,e); throw new WebApplicationException(e); }finally{ if (ses!=null) ses.logout(); } return toReturn ; } @GET @Path("{id}/children") @Produces(MediaType.APPLICATION_JSON) public ItemList listById(@QueryParam("showHidden") Boolean showHidden, @QueryParam("exclude") List excludes){ Session ses = null; List toReturn = null; try{ String login = AuthorizationProvider.instance.get().getClient().getId(); long start = System.currentTimeMillis(); ses = repository.getRepository().login(new SimpleCredentials(login,Utils.getSecurePassword(login).toCharArray())); authChecker.checkReadAuthorizationControl(ses, id); log.info("time to connect to repo {}",(System.currentTimeMillis()-start)); log.info("excludes is {}",excludes); toReturn = Utils.getItemList(ses.getNodeByIdentifier(id), excludes, null, showHidden==null?false:showHidden); }catch(Throwable e){ log.error("error reading the node children of {}",id,e); throw new WebApplicationException(e); }finally{ if (ses!=null) ses.logout(); } return new ItemList(toReturn); } @GET @Path("{id}/children/paged") @Produces(MediaType.APPLICATION_JSON) public ItemList listByIdPaged(@QueryParam("showHidden") Boolean showHidden, @QueryParam("start") Integer start, @QueryParam("limit") Integer limit, @QueryParam("exclude") List excludes){ Session ses = null; List toReturn = null; try{ String login = AuthorizationProvider.instance.get().getClient().getId(); ses = repository.getRepository().login(new SimpleCredentials(login,Utils.getSecurePassword(login).toCharArray())); authChecker.checkReadAuthorizationControl(ses, id); log.info("time to connect to repo {}",(System.currentTimeMillis()-start)); log.info("excludes is {}",excludes); toReturn = Utils.getItemList(ses.getNodeByIdentifier(id), excludes, new Range(start, limit),showHidden==null?false:showHidden); }catch(Throwable e){ log.error("error reading the node children of {}",id,e); throw new WebApplicationException(e); }finally{ if (ses!=null) ses.logout(); } return new ItemList(toReturn); } @GET @Path("{id}/publiclink") public URL getPubliclink() { //TODO: check who can call this method Session ses = null; try{ String login = AuthorizationProvider.instance.get().getClient().getId(); ses = repository.getRepository().login(new SimpleCredentials(login,Utils.getSecurePassword(login).toCharArray())); authChecker.checkReadAuthorizationControl(ses, id); String url = Utils.getStorageClient(login).getClient().getHttpsUrl().RFileById(id); return new URL(url); }catch(Throwable e){ log.error("error reading the node children of {}",id,e); throw new WebApplicationException(e); }finally{ if (ses!=null) ses.logout(); } } @GET @Path("{id}/download") public Response download(){ Session ses = null; try{ final String login = AuthorizationProvider.instance.get().getClient().getId(); long start = System.currentTimeMillis(); ses = repository.getRepository().login(new SimpleCredentials(context.getInitParameter(Constants.ADMIN_PARAM_NAME),context.getInitParameter(Constants.ADMIN_PARAM_PWD).toCharArray())); log.info("time to connect to repo {}",(System.currentTimeMillis()-start)); final Node node = ses.getNodeByIdentifier(id); authChecker.checkReadAuthorizationControl(ses, id); final Item item = ItemHandler.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); return Response .ok(so) .header("content-disposition","attachment; filename = "+fileItem.getName()) .header("Content-Length", fileItem.getContent().getSize()) .build(); } else if (item instanceof FolderItem){ try { final Deque allNodes = Utils.getAllNodesForZip((FolderItem)item, ses, accountingHandler); final org.gcube.common.storagehub.model.Path originalPath = Paths.getPath(item.getPath()); StreamingOutput so = new StreamingOutput() { @Override public void write(OutputStream os) { try(ZipOutputStream zos = new ZipOutputStream(os)){ long start = System.currentTimeMillis(); zos.setLevel(Deflater.BEST_COMPRESSION); log.debug("writing StreamOutput"); Utils.zipNode(zos, allNodes, login, originalPath); log.debug("StreamOutput written in {}",(System.currentTimeMillis()-start)); } catch (Exception e) { log.error("error writing stream",e); } } }; return Response .ok(so) .header("content-disposition","attachment; filename = directory.zip") .header("Content-Length", -1l) .build(); }finally { if (ses!=null) ses.save(); } } else throw new Exception("item type not supported for download: "+item.getClass()); }catch(Exception e ){ log.error("error downloading item content",e); throw new WebApplicationException(e); } finally{ if (ses!=null) ses.logout(); } } @PUT @Path("{id}/move") public Response move(@QueryParam("newpath") String path, @PathParam("id") String identifier){ Session ses = null; try{ final String login = AuthorizationProvider.instance.get().getClient().getId(); long start = System.currentTimeMillis(); //ses = RepositoryInitializer.getRepository().login(new SimpleCredentials(login,Utils.getSecurePassword(login).toCharArray())); //TODO check if it is possible to change all the ACL on a workspace ses = repository.getRepository().login(new SimpleCredentials(context.getInitParameter(Constants.ADMIN_PARAM_NAME),context.getInitParameter(Constants.ADMIN_PARAM_PWD).toCharArray())); authChecker.checkReadAuthorizationControl(ses, id); log.info("time to connect to repo {}",(System.currentTimeMillis()-start)); final Node nodeToMove = ses.getNodeByIdentifier(identifier); final Node destination = ses.getNode(path); Item destinationItem = ItemHandler.getItem(destination,null); //TODO for now only owner of the destination folder can move file if (!destinationItem.getOwner().equals(login)){ /*AccessControlManager accessControlManager = ses.getAccessControlManager(); boolean canWrite = accessControlManager.hasPrivileges(path, new Privilege[] { accessControlManager.privilegeFromName(Privilege.JCR_ADD_CHILD_NODES)});*/ //if (!canWrite) throw new IllegalAccessException("Insufficent Provileges to write in "+path); } final Item item = ItemHandler.getItem(nodeToMove, null); if (item instanceof SharedFolder){ throw new Exception("shared folder cannot be moved"); }else if (item instanceof FolderItem){ if (hasSharedChildren((FolderItem) item, ses)) throw new Exception("folder item with shared children cannot be moved"); ses.getWorkspace().move(nodeToMove.getPath(), destination.getPath()+"/"+nodeToMove.getName()); }else { item.setParentId(destinationItem.getId()); ses.getWorkspace().move(nodeToMove.getPath(), destination.getPath()+"/"+nodeToMove.getName()); } ses.save(); }catch(Exception e){ log.error("error moving item with id {} in path {}",identifier, path,e); throw new WebApplicationException(e); } finally{ if (ses!=null) ses.logout(); } return Response.ok().build(); } private boolean hasSharedChildren(FolderItem item, Session session) throws Exception{ Node currentNode = session.getNodeByIdentifier(item.getId()); for (Item children : Utils.getItemList(currentNode,Arrays.asList("hl:accounting","jcr:content"), null, false)){ if (children instanceof FolderItem) return (children instanceof SharedFolder) || hasSharedChildren((FolderItem)item, session); } return false; } }