Loading src/main/java/it/inaf/oats/vospace/UriService.java +25 −4 Original line number Diff line number Diff line Loading @@ -49,6 +49,9 @@ public class UriService { @Value("${file-service-url}") private String fileServiceUrl; @Value("${link-max-depth}") private int linkMaxDepth; @Autowired private NodeDAO nodeDao; Loading Loading @@ -175,6 +178,8 @@ public class UriService { break; case pullFromVoSpace: // Refresh relative path: it can differ in case of links relativePath = NodeUtils.getVosPath(node); if (!NodeUtils.checkIfReadable(node, creator, groups)) { throw PermissionDeniedException.forPath(relativePath); } Loading Loading @@ -278,9 +283,25 @@ public class UriService { } private Node followLink(LinkNode linkNode) { return this.followLinkRecursive(linkNode, 0); } private Node followLinkRecursive(LinkNode linkNode, int depth) { if(depth >= linkMaxDepth) { throw new InternalFaultException("Max link depth reached at link node: " + NodeUtils.getVosPath(linkNode)); } String targetPath = URIUtils.returnVosPathFromNodeURI(linkNode.getTarget(), authority); Optional<Node> targetNode = nodeDao.listNode(targetPath); return targetNode.orElseThrow(() -> new InternalFaultException("Broken Link to target: " + targetPath)); Optional<Node> targetNodeOpt = nodeDao.listNode(targetPath); Node targetNode = targetNodeOpt.orElseThrow(() -> new InternalFaultException("Broken Link to target: " + targetPath)); if(targetNode instanceof LinkNode) { return this.followLinkRecursive(linkNode, ++depth); } else { return targetNode; } } } src/main/resources/application.properties +3 −0 Original line number Diff line number Diff line Loading @@ -18,4 +18,7 @@ logging.level.org.springframework.web=TRACE vospace-authority=example.com!vospace #tune max depth for chained links link-max-depth=10 file-service-url=http://localhost:8087 src/test/java/it/inaf/oats/vospace/TransferControllerTest.java +6 −5 Original line number Diff line number Diff line Loading @@ -114,7 +114,7 @@ public class TransferControllerTest { @Test public void testPullFromVoSpaceSync() throws Exception { Node node = mockPublicDataNode(); Node node = mockPublicDataNode("vos://example.com!vospace/mynode"); when(nodeDao.listNode(eq("/mynode"))).thenReturn(Optional.of(node)); String requestBody = getResourceFileContent("pullFromVoSpace.xml"); Loading Loading @@ -198,7 +198,7 @@ public class TransferControllerTest { private void testVoSpaceAsyncTransfer(String path, String requestBody) throws Exception { Node node = mockPublicDataNode(); Node node = mockPublicDataNode(path); when(nodeDao.listNode(eq(path))).thenReturn(Optional.of(node)); String redirect = mockMvc.perform(post("/transfers?PHASE=RUN") Loading @@ -216,7 +216,7 @@ public class TransferControllerTest { @Test public void testSetJobPhase() throws Exception { Node node = mockPublicDataNode(); Node node = mockPublicDataNode("vos://example.com!vospace/mynode"); when(nodeDao.listNode(eq("/mynode"))).thenReturn(Optional.of(node)); JobSummary job = getFakePendingJob(); Loading Loading @@ -268,8 +268,9 @@ public class TransferControllerTest { assertEquals("PENDING", phase); } private Node mockPublicDataNode() { private Node mockPublicDataNode(String nodeURI) { Node node = new DataNode(); node.setUri(nodeURI); Property property = new Property(); property.setUri("ivo://ivoa.net/vospace/core#publicread"); property.setValue("true"); Loading Loading @@ -415,7 +416,7 @@ public class TransferControllerTest { @Test public void testSyncTransferUrlParamsMode() throws Exception { Node node = mockPublicDataNode(); Node node = mockPublicDataNode("vos://example.com!vospace/mynode"); when(nodeDao.listNode(eq("/mynode"))).thenReturn(Optional.of(node)); mockMvc.perform(get("/synctrans") Loading src/test/java/it/inaf/oats/vospace/UriServiceTest.java +141 −0 Original line number Diff line number Diff line Loading @@ -8,7 +8,9 @@ package it.inaf.oats.vospace; import it.inaf.ia2.aa.ServletRapClient; import it.inaf.ia2.aa.data.User; import it.inaf.oats.vospace.datamodel.NodeProperties; import it.inaf.oats.vospace.datamodel.NodeUtils; import it.inaf.oats.vospace.datamodel.Views; import it.inaf.oats.vospace.exception.InternalFaultException; import it.inaf.oats.vospace.exception.InvalidArgumentException; import it.inaf.oats.vospace.exception.NodeBusyException; import it.inaf.oats.vospace.exception.PermissionDeniedException; Loading @@ -22,6 +24,7 @@ import javax.servlet.http.HttpServletRequest; import net.ivoa.xml.uws.v1.JobSummary; import net.ivoa.xml.vospace.v2.ContainerNode; import net.ivoa.xml.vospace.v2.DataNode; import net.ivoa.xml.vospace.v2.LinkNode; import net.ivoa.xml.vospace.v2.Node; import net.ivoa.xml.vospace.v2.Param; import net.ivoa.xml.vospace.v2.Property; Loading Loading @@ -102,7 +105,10 @@ public class UriServiceTest { @Test public void testPublicUrl() { String dataUri = "vos://example.com!vospace/mydata1"; Node node = new DataNode(); node.setUri(dataUri); Property property = new Property(); property.setUri(NodeProperties.PUBLIC_READ_URI); property.setValue("true"); Loading @@ -119,7 +125,10 @@ public class UriServiceTest { @Test public void testPrivateUrl() { String dataUri = "vos://example.com!vospace/mydata1"; Node node = new DataNode(); node.setUri(dataUri); Property creator = new Property(); creator.setUri(NodeProperties.CREATOR_URI); creator.setValue("user1"); Loading Loading @@ -154,7 +163,10 @@ public class UriServiceTest { @Test public void testPrivateUrlPermissionDenied() { String dataUri = "vos://example.com!vospace/mydata1"; Node node = new DataNode(); node.setUri(dataUri); Property creator = new Property(); creator.setUri(NodeProperties.CREATOR_URI); creator.setValue("user3"); Loading Loading @@ -188,8 +200,10 @@ public class UriServiceTest { @Test public void testPrivateUrlNodeBusy() { String dataUri = "vos://example.com!vospace/mydata1"; DataNode node = new DataNode(); node.setUri(dataUri); Property creator = new Property(); creator.setUri(NodeProperties.CREATOR_URI); creator.setValue("user1"); Loading Loading @@ -275,6 +289,114 @@ public class UriServiceTest { assertEquals("http://file-service/mydata1/mydata2?jobId=job-id2&token=<new-token>", negotiatedTransfer.getProtocols().get(0).getEndpoint()); } @Test public void pullFromLinkNode() { // URI of pull target node String targetOfPull = "vos://example.com!vospace/mylink1"; String targetOfLink = "vos://example.com!vospace/mydummydata1"; // Define node properties Property creator = new Property(); creator.setUri(NodeProperties.CREATOR_URI); creator.setValue("user1"); Property readgroup = new Property(); readgroup.setUri(NodeProperties.GROUP_READ_URI); readgroup.setValue("group1"); // Define link node as target LinkNode lnode = new LinkNode(); lnode.setUri(targetOfPull); lnode.getProperties().add(creator); lnode.getProperties().add(readgroup); lnode.setTarget(targetOfLink); DataNode dnode = new DataNode(); dnode.setUri(targetOfLink); dnode.getProperties().add(creator); dnode.getProperties().add(readgroup); when(nodeDAO.listNode(eq(NodeUtils.getVosPath(lnode)))).thenReturn(Optional.of(lnode)); when(nodeDAO.listNode(eq(NodeUtils.getVosPath(dnode)))).thenReturn(Optional.of(dnode)); User user = mock(User.class); when(user.getAccessToken()).thenReturn("<token>"); when(user.getName()).thenReturn("user1"); when(servletRequest.getUserPrincipal()).thenReturn(user); when(rapClient.exchangeToken(argThat(req -> { assertEquals("<token>", req.getSubjectToken()); assertEquals("http://file-service/mydummydata1", req.getResource()); return true; }), any())).thenReturn("<new-token>"); JobSummary job = getPullFromVoSpaceJob(targetOfPull); Transfer tr = uriService.getTransfer(job); Transfer negotiatedTransfer = uriService.getNegotiatedTransfer(job, tr); assertEquals("http://file-service" + NodeUtils.getVosPath(dnode) + "?jobId=job-id-pull&token=<new-token>", negotiatedTransfer.getProtocols().get(0).getEndpoint()); } @Test public void pullFromCircularLinkNode() { // URI of pull target node String targetOfPull = "vos://example.com!vospace/mylink1"; String targetOfLink = "vos://example.com!vospace/mylink2"; // Define node properties Property creator = new Property(); creator.setUri(NodeProperties.CREATOR_URI); creator.setValue("user1"); Property readgroup = new Property(); readgroup.setUri(NodeProperties.GROUP_READ_URI); readgroup.setValue("group1"); // Define link node as target LinkNode lnode = new LinkNode(); lnode.setUri(targetOfPull); lnode.getProperties().add(creator); lnode.getProperties().add(readgroup); lnode.setTarget(targetOfLink); LinkNode dnode = new LinkNode(); dnode.setUri(targetOfLink); dnode.getProperties().add(creator); dnode.getProperties().add(readgroup); // Circular reference dnode.setTarget(targetOfPull); when(nodeDAO.listNode(eq(NodeUtils.getVosPath(lnode)))).thenReturn(Optional.of(lnode)); when(nodeDAO.listNode(eq(NodeUtils.getVosPath(dnode)))).thenReturn(Optional.of(dnode)); User user = mock(User.class); when(user.getAccessToken()).thenReturn("<token>"); when(user.getName()).thenReturn("user1"); when(servletRequest.getUserPrincipal()).thenReturn(user); when(rapClient.exchangeToken(argThat(req -> { assertEquals("<token>", req.getSubjectToken()); assertEquals("http://file-service/mydummydata1", req.getResource()); return true; }), any())).thenReturn("<new-token>"); JobSummary job = getPullFromVoSpaceJob(targetOfPull); Transfer tr = uriService.getTransfer(job); assertThrows(InternalFaultException.class, () -> { uriService.getNegotiatedTransfer(job, tr); }); } @Test public void setNodeRemoteLocationTest() { Loading Loading @@ -526,4 +648,23 @@ public class UriServiceTest { return job; } private JobSummary getPullFromVoSpaceJob(String target) { Transfer transfer = new Transfer(); transfer.setTarget(target); transfer.setDirection(JobService.JobDirection.pullFromVoSpace.toString()); Protocol protocol = new Protocol(); protocol.setUri("ivo://ivoa.net/vospace/core#httpget"); transfer.getProtocols().add(protocol); JobSummary job = new JobSummary(); job.setJobId("job-id-pull"); JobSummary.JobInfo jobInfo = new JobSummary.JobInfo(); jobInfo.getAny().add(transfer); job.setJobInfo(jobInfo); return job; } } Loading
src/main/java/it/inaf/oats/vospace/UriService.java +25 −4 Original line number Diff line number Diff line Loading @@ -49,6 +49,9 @@ public class UriService { @Value("${file-service-url}") private String fileServiceUrl; @Value("${link-max-depth}") private int linkMaxDepth; @Autowired private NodeDAO nodeDao; Loading Loading @@ -175,6 +178,8 @@ public class UriService { break; case pullFromVoSpace: // Refresh relative path: it can differ in case of links relativePath = NodeUtils.getVosPath(node); if (!NodeUtils.checkIfReadable(node, creator, groups)) { throw PermissionDeniedException.forPath(relativePath); } Loading Loading @@ -278,9 +283,25 @@ public class UriService { } private Node followLink(LinkNode linkNode) { return this.followLinkRecursive(linkNode, 0); } private Node followLinkRecursive(LinkNode linkNode, int depth) { if(depth >= linkMaxDepth) { throw new InternalFaultException("Max link depth reached at link node: " + NodeUtils.getVosPath(linkNode)); } String targetPath = URIUtils.returnVosPathFromNodeURI(linkNode.getTarget(), authority); Optional<Node> targetNode = nodeDao.listNode(targetPath); return targetNode.orElseThrow(() -> new InternalFaultException("Broken Link to target: " + targetPath)); Optional<Node> targetNodeOpt = nodeDao.listNode(targetPath); Node targetNode = targetNodeOpt.orElseThrow(() -> new InternalFaultException("Broken Link to target: " + targetPath)); if(targetNode instanceof LinkNode) { return this.followLinkRecursive(linkNode, ++depth); } else { return targetNode; } } }
src/main/resources/application.properties +3 −0 Original line number Diff line number Diff line Loading @@ -18,4 +18,7 @@ logging.level.org.springframework.web=TRACE vospace-authority=example.com!vospace #tune max depth for chained links link-max-depth=10 file-service-url=http://localhost:8087
src/test/java/it/inaf/oats/vospace/TransferControllerTest.java +6 −5 Original line number Diff line number Diff line Loading @@ -114,7 +114,7 @@ public class TransferControllerTest { @Test public void testPullFromVoSpaceSync() throws Exception { Node node = mockPublicDataNode(); Node node = mockPublicDataNode("vos://example.com!vospace/mynode"); when(nodeDao.listNode(eq("/mynode"))).thenReturn(Optional.of(node)); String requestBody = getResourceFileContent("pullFromVoSpace.xml"); Loading Loading @@ -198,7 +198,7 @@ public class TransferControllerTest { private void testVoSpaceAsyncTransfer(String path, String requestBody) throws Exception { Node node = mockPublicDataNode(); Node node = mockPublicDataNode(path); when(nodeDao.listNode(eq(path))).thenReturn(Optional.of(node)); String redirect = mockMvc.perform(post("/transfers?PHASE=RUN") Loading @@ -216,7 +216,7 @@ public class TransferControllerTest { @Test public void testSetJobPhase() throws Exception { Node node = mockPublicDataNode(); Node node = mockPublicDataNode("vos://example.com!vospace/mynode"); when(nodeDao.listNode(eq("/mynode"))).thenReturn(Optional.of(node)); JobSummary job = getFakePendingJob(); Loading Loading @@ -268,8 +268,9 @@ public class TransferControllerTest { assertEquals("PENDING", phase); } private Node mockPublicDataNode() { private Node mockPublicDataNode(String nodeURI) { Node node = new DataNode(); node.setUri(nodeURI); Property property = new Property(); property.setUri("ivo://ivoa.net/vospace/core#publicread"); property.setValue("true"); Loading Loading @@ -415,7 +416,7 @@ public class TransferControllerTest { @Test public void testSyncTransferUrlParamsMode() throws Exception { Node node = mockPublicDataNode(); Node node = mockPublicDataNode("vos://example.com!vospace/mynode"); when(nodeDao.listNode(eq("/mynode"))).thenReturn(Optional.of(node)); mockMvc.perform(get("/synctrans") Loading
src/test/java/it/inaf/oats/vospace/UriServiceTest.java +141 −0 Original line number Diff line number Diff line Loading @@ -8,7 +8,9 @@ package it.inaf.oats.vospace; import it.inaf.ia2.aa.ServletRapClient; import it.inaf.ia2.aa.data.User; import it.inaf.oats.vospace.datamodel.NodeProperties; import it.inaf.oats.vospace.datamodel.NodeUtils; import it.inaf.oats.vospace.datamodel.Views; import it.inaf.oats.vospace.exception.InternalFaultException; import it.inaf.oats.vospace.exception.InvalidArgumentException; import it.inaf.oats.vospace.exception.NodeBusyException; import it.inaf.oats.vospace.exception.PermissionDeniedException; Loading @@ -22,6 +24,7 @@ import javax.servlet.http.HttpServletRequest; import net.ivoa.xml.uws.v1.JobSummary; import net.ivoa.xml.vospace.v2.ContainerNode; import net.ivoa.xml.vospace.v2.DataNode; import net.ivoa.xml.vospace.v2.LinkNode; import net.ivoa.xml.vospace.v2.Node; import net.ivoa.xml.vospace.v2.Param; import net.ivoa.xml.vospace.v2.Property; Loading Loading @@ -102,7 +105,10 @@ public class UriServiceTest { @Test public void testPublicUrl() { String dataUri = "vos://example.com!vospace/mydata1"; Node node = new DataNode(); node.setUri(dataUri); Property property = new Property(); property.setUri(NodeProperties.PUBLIC_READ_URI); property.setValue("true"); Loading @@ -119,7 +125,10 @@ public class UriServiceTest { @Test public void testPrivateUrl() { String dataUri = "vos://example.com!vospace/mydata1"; Node node = new DataNode(); node.setUri(dataUri); Property creator = new Property(); creator.setUri(NodeProperties.CREATOR_URI); creator.setValue("user1"); Loading Loading @@ -154,7 +163,10 @@ public class UriServiceTest { @Test public void testPrivateUrlPermissionDenied() { String dataUri = "vos://example.com!vospace/mydata1"; Node node = new DataNode(); node.setUri(dataUri); Property creator = new Property(); creator.setUri(NodeProperties.CREATOR_URI); creator.setValue("user3"); Loading Loading @@ -188,8 +200,10 @@ public class UriServiceTest { @Test public void testPrivateUrlNodeBusy() { String dataUri = "vos://example.com!vospace/mydata1"; DataNode node = new DataNode(); node.setUri(dataUri); Property creator = new Property(); creator.setUri(NodeProperties.CREATOR_URI); creator.setValue("user1"); Loading Loading @@ -275,6 +289,114 @@ public class UriServiceTest { assertEquals("http://file-service/mydata1/mydata2?jobId=job-id2&token=<new-token>", negotiatedTransfer.getProtocols().get(0).getEndpoint()); } @Test public void pullFromLinkNode() { // URI of pull target node String targetOfPull = "vos://example.com!vospace/mylink1"; String targetOfLink = "vos://example.com!vospace/mydummydata1"; // Define node properties Property creator = new Property(); creator.setUri(NodeProperties.CREATOR_URI); creator.setValue("user1"); Property readgroup = new Property(); readgroup.setUri(NodeProperties.GROUP_READ_URI); readgroup.setValue("group1"); // Define link node as target LinkNode lnode = new LinkNode(); lnode.setUri(targetOfPull); lnode.getProperties().add(creator); lnode.getProperties().add(readgroup); lnode.setTarget(targetOfLink); DataNode dnode = new DataNode(); dnode.setUri(targetOfLink); dnode.getProperties().add(creator); dnode.getProperties().add(readgroup); when(nodeDAO.listNode(eq(NodeUtils.getVosPath(lnode)))).thenReturn(Optional.of(lnode)); when(nodeDAO.listNode(eq(NodeUtils.getVosPath(dnode)))).thenReturn(Optional.of(dnode)); User user = mock(User.class); when(user.getAccessToken()).thenReturn("<token>"); when(user.getName()).thenReturn("user1"); when(servletRequest.getUserPrincipal()).thenReturn(user); when(rapClient.exchangeToken(argThat(req -> { assertEquals("<token>", req.getSubjectToken()); assertEquals("http://file-service/mydummydata1", req.getResource()); return true; }), any())).thenReturn("<new-token>"); JobSummary job = getPullFromVoSpaceJob(targetOfPull); Transfer tr = uriService.getTransfer(job); Transfer negotiatedTransfer = uriService.getNegotiatedTransfer(job, tr); assertEquals("http://file-service" + NodeUtils.getVosPath(dnode) + "?jobId=job-id-pull&token=<new-token>", negotiatedTransfer.getProtocols().get(0).getEndpoint()); } @Test public void pullFromCircularLinkNode() { // URI of pull target node String targetOfPull = "vos://example.com!vospace/mylink1"; String targetOfLink = "vos://example.com!vospace/mylink2"; // Define node properties Property creator = new Property(); creator.setUri(NodeProperties.CREATOR_URI); creator.setValue("user1"); Property readgroup = new Property(); readgroup.setUri(NodeProperties.GROUP_READ_URI); readgroup.setValue("group1"); // Define link node as target LinkNode lnode = new LinkNode(); lnode.setUri(targetOfPull); lnode.getProperties().add(creator); lnode.getProperties().add(readgroup); lnode.setTarget(targetOfLink); LinkNode dnode = new LinkNode(); dnode.setUri(targetOfLink); dnode.getProperties().add(creator); dnode.getProperties().add(readgroup); // Circular reference dnode.setTarget(targetOfPull); when(nodeDAO.listNode(eq(NodeUtils.getVosPath(lnode)))).thenReturn(Optional.of(lnode)); when(nodeDAO.listNode(eq(NodeUtils.getVosPath(dnode)))).thenReturn(Optional.of(dnode)); User user = mock(User.class); when(user.getAccessToken()).thenReturn("<token>"); when(user.getName()).thenReturn("user1"); when(servletRequest.getUserPrincipal()).thenReturn(user); when(rapClient.exchangeToken(argThat(req -> { assertEquals("<token>", req.getSubjectToken()); assertEquals("http://file-service/mydummydata1", req.getResource()); return true; }), any())).thenReturn("<new-token>"); JobSummary job = getPullFromVoSpaceJob(targetOfPull); Transfer tr = uriService.getTransfer(job); assertThrows(InternalFaultException.class, () -> { uriService.getNegotiatedTransfer(job, tr); }); } @Test public void setNodeRemoteLocationTest() { Loading Loading @@ -526,4 +648,23 @@ public class UriServiceTest { return job; } private JobSummary getPullFromVoSpaceJob(String target) { Transfer transfer = new Transfer(); transfer.setTarget(target); transfer.setDirection(JobService.JobDirection.pullFromVoSpace.toString()); Protocol protocol = new Protocol(); protocol.setUri("ivo://ivoa.net/vospace/core#httpget"); transfer.getProtocols().add(protocol); JobSummary job = new JobSummary(); job.setJobId("job-id-pull"); JobSummary.JobInfo jobInfo = new JobSummary.JobInfo(); jobInfo.getAny().add(transfer); job.setJobInfo(jobInfo); return job; } }