Skip to content
UriServiceTest.java 23.6 KiB
Newer Older
Sonia Zorba's avatar
Sonia Zorba committed
/*
 * This file is part of vospace-rest
 * Copyright (C) 2021 Istituto Nazionale di Astrofisica
 * SPDX-License-Identifier: GPL-3.0-or-later
 */
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;
import it.inaf.oats.vospace.exception.ProtocolNotSupportedException;
Nicola Fulvio Calabria's avatar
Nicola Fulvio Calabria committed
import it.inaf.oats.vospace.persistence.LinkedServiceDAO;
import it.inaf.oats.vospace.persistence.LocationDAO;
import it.inaf.oats.vospace.persistence.NodeDAO;
import it.inaf.oats.vospace.persistence.model.Location;
import it.inaf.oats.vospace.persistence.model.LocationType;
import java.util.Optional;
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;
import net.ivoa.xml.vospace.v2.Transfer;
import net.ivoa.xml.vospace.v2.View;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
import org.springframework.test.context.TestPropertySource;

@SpringBootTest
@AutoConfigureMockMvc
@TestPropertySource(properties = {"vospace-authority=example.com!vospace", "file-service-url=http://file-service"})
public class UriServiceTest {

    @MockBean
    private NodeDAO nodeDAO;
    @MockBean
    private LocationDAO locationDAO;
    @MockBean
    private ServletRapClient rapClient;
    private CreateNodeService createNodeService;
Nicola Fulvio Calabria's avatar
Nicola Fulvio Calabria committed
    
    @MockBean
    private LinkedServiceDAO linkedServiceDAO;
    @MockBean
    private FileServiceClient fileServiceClient;

    @Autowired
    private HttpServletRequest servletRequest;

    @Autowired
    private UriService uriService;

    @TestConfiguration
    public static class TestConfig {

        /**
         * Necessary because MockBean doesn't work with HttpServletRequest.
         */
        @Bean
        @Primary
        public HttpServletRequest servletRequest() {
            HttpServletRequest request = mock(HttpServletRequest.class);
            User user = new User().setUserId("anonymous");
            when(request.getUserPrincipal()).thenReturn(user);
            return request;
Nicola Fulvio Calabria's avatar
Nicola Fulvio Calabria committed
    
    public final static String PORTAL_URL = "http://portalurl.ia2.inaf.it/portal/files";
    
    @BeforeEach
    public void init() {
        Location location = new Location();
        location.setType(LocationType.ASYNC);
        when(locationDAO.getNodeLocation(any())).thenReturn(Optional.of(location));
Nicola Fulvio Calabria's avatar
Nicola Fulvio Calabria committed
        when(linkedServiceDAO.isLinkedServiceUrl(eq(PORTAL_URL))).thenReturn(Boolean.TRUE);       

    @Test
    public void testPublicUrl() {
        
        String dataUri = "vos://example.com!vospace/mydata1";
        Property property = new Property();
        property.setUri(NodeProperties.PUBLIC_READ_URI);
        property.setValue("true");
        node.getProperties().add(property);

        when(nodeDAO.listNode(eq("/mydata1"))).thenReturn(Optional.of(node));
        Transfer negotiatedTransfer = uriService.getNegotiatedTransfer(job, uriService.getTransfer(job));
        assertEquals("http://file-service/mydata1?jobId=job-id", negotiatedTransfer.getProtocols().get(0).getEndpoint());
    public void testPrivateUrl() {
        
        String dataUri = "vos://example.com!vospace/mydata1";

        Node node = new DataNode();
        Property creator = new Property();
        creator.setUri(NodeProperties.CREATOR_URI);
        creator.setValue("user1");
        node.getProperties().add(creator);
        Property readgroup = new Property();
        readgroup.setUri(NodeProperties.GROUP_READ_URI);
        readgroup.setValue("group1");
        node.getProperties().add(readgroup);
        when(nodeDAO.listNode(eq("/mydata1"))).thenReturn(Optional.of(node));
        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/mydata1", req.getResource());
            return true;
        }), any())).thenReturn("<new-token>");

        JobSummary job = getJob();
        Transfer tr = uriService.getTransfer(job);
        Transfer negotiatedTransfer = uriService.getNegotiatedTransfer(job, tr);
        assertEquals("http://file-service/mydata1?jobId=job-id&token=<new-token>", negotiatedTransfer.getProtocols().get(0).getEndpoint());
    @Test
    public void testPrivateUrlPermissionDenied() {
        
        String dataUri = "vos://example.com!vospace/mydata1";
        Property creator = new Property();
        creator.setUri(NodeProperties.CREATOR_URI);
        creator.setValue("user3");
        node.getProperties().add(creator);

        Property readgroup = new Property();
        readgroup.setUri(NodeProperties.GROUP_READ_URI);
        readgroup.setValue("group1000");
        node.getProperties().add(readgroup);

        when(nodeDAO.listNode(eq("/mydata1"))).thenReturn(Optional.of(node));

        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/mydata1", req.getResource());
            return true;
        }), any())).thenReturn("<new-token>");

        JobSummary job = getJob();
        Transfer tr = uriService.getTransfer(job);
        assertThrows(PermissionDeniedException.class, () -> {
            uriService.getNegotiatedTransfer(job, tr);
        });
    @Test
    public void testPrivateUrlNodeBusy() {
        String dataUri = "vos://example.com!vospace/mydata1";
        Property creator = new Property();
        creator.setUri(NodeProperties.CREATOR_URI);
        creator.setValue("user1");
        node.getProperties().add(creator);

        Property readgroup = new Property();
        readgroup.setUri(NodeProperties.GROUP_READ_URI);
        readgroup.setValue("group1");
        node.getProperties().add(readgroup);

        when(nodeDAO.listNode(eq("/mydata1"))).thenReturn(Optional.of(node));

        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/mydata1", req.getResource());
            return true;
        }), any())).thenReturn("<new-token>");

        JobSummary job = getJob();
        Transfer tr = uriService.getTransfer(job);
        assertThrows(NodeBusyException.class, () -> {
            uriService.getNegotiatedTransfer(job, tr);
        });
        ContainerNode node = new ContainerNode();
        node.setUri("vos://example.com!vospace/mydata1");
        Property creator = new Property();
        creator.setUri(NodeProperties.CREATOR_URI);
        creator.setValue("user1");
        node.getProperties().add(creator);
        Property readgroup = new Property();
        readgroup.setUri(NodeProperties.GROUP_READ_URI);
        readgroup.setValue("group1");
        node.getProperties().add(readgroup);
        DataNode dnode = new DataNode();
        dnode.setUri("vos://example.com!vospace/mydata1/mydata2");
        dnode.getProperties().add(creator);
        dnode.getProperties().add(readgroup);
        Property writegroup = new Property();
        writegroup.setUri(NodeProperties.GROUP_WRITE_URI);
        writegroup.setValue("group1");
        dnode.getProperties().add(writegroup);
        when(nodeDAO.listNode(eq("/mydata1"))).thenReturn(Optional.of(node));
        when(nodeDAO.listNode(eq("/mydata1/mydata2"))).thenReturn(Optional.empty());
        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/mydata1/mydata2", req.getResource());
            return true;
        }), any())).thenReturn("<new-token>");

        JobSummary job = getPushToVoSpaceJob();
        Transfer tr = uriService.getTransfer(job);
        when(createNodeService.createNode(any(), any(), eq(user))).thenReturn(dnode);
        Transfer negotiatedTransfer = uriService.getNegotiatedTransfer(job, tr);
        verify(createNodeService, times(1)).createNode(any(), any(), eq(user));
        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);
        JobSummary job = getPullFromVoSpaceJob(targetOfPull);
        Transfer tr = uriService.getTransfer(job);
        
        assertThrows(InternalFaultException.class, () -> {
            uriService.getNegotiatedTransfer(job, tr);
        });
    }
    
    @Test
    public void setNodeRemoteLocationTest() {

        String nodeUri = "vos://example.com!vospace/test/f1/lbtfile.fits";
        String contentUri = "http://archive.lbto.org/files/lbtfile.fits";

        Location location = new Location();
        location.setId(5);

        when(locationDAO.findPortalLocation(eq("archive.lbto.org"))).thenReturn(Optional.of(location));

        uriService.setNodeRemoteLocation(nodeUri, contentUri);

        verify(nodeDAO).setNodeLocation(eq("/test/f1/lbtfile.fits"), eq(5), eq("lbtfile.fits"));
    }

    @Test
    public void testSetSyncTransferEndpointsPullFromVoSpace() {

        mockPublicNode();

        Transfer transfer = new Transfer();
        transfer.setTarget("vos://example.com!vospace/mydata1");
        transfer.setDirection("pullFromVoSpace");

        Protocol protocol1 = new Protocol();
        protocol1.setUri("ivo://ivoa.net/vospace/core#httpget");
        transfer.getProtocols().add(protocol1);
        Protocol protocol2 = new Protocol();

        protocol2.setUri("invalid_protocol");
        transfer.getProtocols().add(protocol2);

        JobSummary job = new JobSummary();
        JobSummary.JobInfo jobInfo = new JobSummary.JobInfo();
        jobInfo.getAny().add(transfer);
        job.setJobInfo(jobInfo);

        assertEquals(2, transfer.getProtocols().size());

        Transfer negotiatedTransfer = uriService.getNegotiatedTransfer(job, transfer);
        assertEquals(1, negotiatedTransfer.getProtocols().size());
        assertEquals("ivo://ivoa.net/vospace/core#httpget", negotiatedTransfer.getProtocols().get(0).getUri());
    }

    @Test
    public void testSetSyncTransferEndpointsPushToVoSpace() {

        Node node = mockPublicNode();

        Property creator = new Property();
        creator.setUri(NodeProperties.CREATOR_URI);
        creator.setValue("user1");

        node.getProperties().add(creator);
        User user = mock(User.class);
        when(user.getName()).thenReturn("user1");
        when(servletRequest.getUserPrincipal()).thenReturn(user);

        Transfer transfer = new Transfer();
        transfer.setTarget("vos://example.com!vospace/mydata1");
        transfer.setDirection("pushToVoSpace");

        Protocol protocol1 = new Protocol();
        protocol1.setUri("ivo://ivoa.net/vospace/core#httpput");
        transfer.getProtocols().add(protocol1);
        Protocol protocol2 = new Protocol();

        protocol2.setUri("invalid_protocol");
        transfer.getProtocols().add(protocol2);

        JobSummary job = new JobSummary();
        JobSummary.JobInfo jobInfo = new JobSummary.JobInfo();
        jobInfo.getAny().add(transfer);
        job.setJobInfo(jobInfo);

        assertEquals(2, transfer.getProtocols().size());

        Transfer negotiatedTransfer = uriService.getNegotiatedTransfer(job, transfer);
        assertEquals(1, negotiatedTransfer.getProtocols().size());
        assertEquals("ivo://ivoa.net/vospace/core#httpput", negotiatedTransfer.getProtocols().get(0).getUri());
    }

    @Test
    public void testSetSyncTransferEndpointsUnsupportedProtocol() {

        Transfer transfer = new Transfer();
        transfer.setTarget("vos://example.com!vospace/mydata1");
        transfer.setDirection("pullFromVoSpace");

        Protocol protocol = new Protocol();
        protocol.setUri("invalid_protocol");
        transfer.getProtocols().add(protocol);

        JobSummary job = new JobSummary();
        JobSummary.JobInfo jobInfo = new JobSummary.JobInfo();
        jobInfo.getAny().add(transfer);
        job.setJobInfo(jobInfo);

        try {
            uriService.getNegotiatedTransfer(job, transfer);
            fail("Expected ProtocolNotSupportedException");
        } catch (ProtocolNotSupportedException ex) {
        }
    }

    @Test
    public void testSetSyncTransferEndpointsNoProtocols() {

        Transfer transfer = new Transfer();
        transfer.setTarget("vos://example.com!vospace/mydata1");
        transfer.setDirection("pullFromVoSpace");

        JobSummary job = new JobSummary();
        JobSummary.JobInfo jobInfo = new JobSummary.JobInfo();
        jobInfo.getAny().add(transfer);
        job.setJobInfo(jobInfo);

        try {
            uriService.getNegotiatedTransfer(job, transfer);
            fail("Expected InvalidArgumentException");
        } catch (InvalidArgumentException ex) {
        }
    }

    @Test
    public void testTarArchiveViewEndpoint() {
        testArchiveViewEndpoint(Views.TAR_VIEW_URI);
    }

    @Test
    public void testZipArchiveViewEndpoint() {
        testArchiveViewEndpoint(Views.ZIP_VIEW_URI);
    }
    @Test
    public void testInvalidTransferNoProtocols() {

        Transfer transfer = new Transfer();
        transfer.setDirection("pullFromVoSpace");
        transfer.setTarget("vos://example.com!vospace/file1");

        JobSummary job = new JobSummary();
        JobSummary.JobInfo jobInfo = new JobSummary.JobInfo();
        jobInfo.getAny().add(transfer);
        job.setJobInfo(jobInfo);

        mockPublicNode("file1");
        mockPublicNode("file2");

        InvalidArgumentException ex = assertThrows(InvalidArgumentException.class, () -> {
            uriService.getNegotiatedTransfer(job, transfer);
        });
        assertTrue(ex.getMessage().contains("no protocol"));
    }

    private void testArchiveViewEndpoint(String viewUri) {

        Transfer transfer = new Transfer();
        transfer.setDirection("pullFromVoSpace");
        transfer.setTarget("vos://example.com!vospace/parent_dir");
        Protocol protocol = new Protocol();
        protocol.setUri("ivo://ivoa.net/vospace/core#httpget");
        transfer.getProtocols().add(protocol);
        View view = new View();
        view.setUri(viewUri);

        Param param1 = new Param();
        param1.setUri(viewUri + "/include");
        param1.setValue("file1");
        view.getParam().add(param1);

        Param param2 = new Param();
        param2.setUri(viewUri + "/include");
        param2.setValue("file2");
        view.getParam().add(param2);

        transfer.setView(view);

        JobSummary job = new JobSummary();
        job.setJobId("archive-job-id");
        JobSummary.JobInfo jobInfo = new JobSummary.JobInfo();
        jobInfo.getAny().add(transfer);
        job.setJobInfo(jobInfo);

        mockPublicNode("parent_dir");
        mockPublicNode("parent_dir/file1");
        mockPublicNode("parent_dir/file2");
Nicola Fulvio Calabria's avatar
Nicola Fulvio Calabria committed
        
        when(fileServiceClient.startArchiveJob(any(), any()))
                .thenReturn("http://file-service/mockarchive");
        uriService.getNegotiatedTransfer(job, transfer);
        verify(fileServiceClient, times(1)).startArchiveJob(transfer, "archive-job-id");
    }

        return mockPublicNode("mydata1");
    }

    private Node mockPublicNode(String name) {
        node.setUri("vos://example.com!vospace/" + name);
        Property publicProperty = new Property();
        publicProperty.setUri(NodeProperties.PUBLIC_READ_URI);
        publicProperty.setValue(String.valueOf(true));
        node.getProperties().add(publicProperty);
        when(nodeDAO.listNode(eq("/" + name))).thenReturn(Optional.of(node));
        Transfer transfer = new Transfer();
        transfer.setTarget("vos://example.com!vospace/mydata1");
        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");

        JobSummary.JobInfo jobInfo = new JobSummary.JobInfo();
        jobInfo.getAny().add(transfer);

        job.setJobInfo(jobInfo);

    private JobSummary getPushToVoSpaceJob() {
        Transfer transfer = new Transfer();
        transfer.setTarget("vos://example.com!vospace/mydata1/mydata2");
        transfer.setDirection(JobService.JobDirection.pushToVoSpace.toString());
        Protocol protocol = new Protocol();
        protocol.setUri("ivo://ivoa.net/vospace/core#httpput");
        transfer.getProtocols().add(protocol);

        JobSummary job = new JobSummary();
        job.setJobId("job-id2");

        JobSummary.JobInfo jobInfo = new JobSummary.JobInfo();
        jobInfo.getAny().add(transfer);

        job.setJobInfo(jobInfo);

    
    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;
    }