Commit 100137ca authored by Sonia Zorba's avatar Sonia Zorba
Browse files

Improved validation of list of links upload

parent d93836d2
Loading
Loading
Loading
Loading
Loading
+37 −3
Original line number Diff line number Diff line
@@ -10,6 +10,8 @@ import it.inaf.ia2.vospace.ui.data.CreateLinkRequest;
import it.inaf.ia2.vospace.ui.exception.BadRequestException;
import it.inaf.ia2.vospace.ui.exception.VOSpaceStatusException;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
@@ -18,6 +20,7 @@ import net.ivoa.xml.vospace.v2.LinkNode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
@@ -31,6 +34,9 @@ public class CreateLinksController extends BaseController {

    private static final Logger LOG = LoggerFactory.getLogger(CreateLinksController.class);

    @Value("${list-of-links.limit:1000}")
    private int listOfLinksSizeLimit;

    @Autowired
    private VOSpaceClient client;

@@ -63,11 +69,17 @@ public class CreateLinksController extends BaseController {
        List<CompletableFuture<?>> currentHttpCallsGroup = new ArrayList<>();
        httpCallsGroups.add(currentHttpCallsGroup);

        for (String url : fileContent.replaceAll("\\r\\n?", "\n").split("\n")) { // normalize newlines and split on them
        // normalize newlines and split on them
        String[] urls = fileContent.replaceAll("\\r\\n?", "\n").split("\n");
        if (urls.length > listOfLinksSizeLimit) {
            throw new BadRequestException("List is too large: " + urls.length + " lines detected, limit is " + listOfLinksSizeLimit);
        }

        for (String url : urls) {
            if (!url.isBlank()) {

                String fileName = url.substring(url.lastIndexOf("/") + 1);
                String uri = parent.getUri() + "/" + fileName;
                url = url.trim();
                String uri = parent.getUri() + "/" + getFileNameFromUrl(url);

                LinkNode link = new LinkNode();
                link.setUri(uri);
@@ -89,6 +101,28 @@ public class CreateLinksController extends BaseController {
        return ResponseEntity.noContent().build();
    }

    private String getFileNameFromUrl(String url) {

        try {
            // parse URL and remove the query string
            String urlPath = new URL(url).getPath();
            if (urlPath.endsWith("/")) {
                // remove last char if it is a slash
                urlPath = urlPath.substring(0, urlPath.length() - 1);
            }
            if (urlPath.isEmpty() || !urlPath.contains("/")) {
                throw new BadRequestException("Unable to extract file name from URL " + url);
            }
            String fileName = urlPath.substring(urlPath.lastIndexOf("/") + 1);
            if (fileName.isEmpty()) {
                throw new BadRequestException("Unable to extract file name from URL " + url);
            }
            return fileName;
        } catch (MalformedURLException ex) {
            throw new BadRequestException("Invalid URL: " + url);
        }
    }

    private ContainerNode getFolder(String folderPath) {
        try {
            return (ContainerNode) client.getNode("/" + folderPath);
+1 −0
Original line number Diff line number Diff line
@@ -11,6 +11,7 @@ cors.allowed.origin=http://localhost:8080
logging.level.it.inaf=TRACE

trusted.eppn.scope=inaf.it
list-of-links.limit=1000

support.contact.label=IA2 team
support.contact.email=ia2@inaf.it
+36 −0
Original line number Diff line number Diff line
@@ -10,6 +10,7 @@ import it.inaf.ia2.aa.data.User;
import it.inaf.ia2.vospace.ui.client.VOSpaceClient;
import it.inaf.ia2.vospace.ui.data.CreateLinkRequest;
import it.inaf.ia2.vospace.ui.exception.VOSpaceStatusException;
import java.util.Collections;
import net.ivoa.xml.vospace.v2.ContainerNode;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@@ -109,6 +110,41 @@ public class CreateLinksControllerTest {
        verify(client, times(75)).createNode(any());
    }

    @Test
    public void testInvalidUrl() throws Exception {
        testInvalidContent("foo");
    }

    @Test
    public void testInvalidUrlNoFile() throws Exception {
        testInvalidContent("http://archives.ia2.inaf.it/");
    }

    @Test
    public void testInvalidUrlNoFileNoSlash() throws Exception {
        testInvalidContent("http://archives.ia2.inaf.it");
    }

    @Test
    public void testTooManyLinks() throws Exception {
        testInvalidContent(String.join("\n", Collections.nCopies(1500, "http://archives.ia2.inaf.it/files/aao/SC182172.fits.gz")));
    }

    private void testInvalidContent(String fileContent) throws Exception {

        ContainerNode myFolder = new ContainerNode();
        when(client.getNode("/path/to/myfolder")).thenReturn(myFolder);

        MockMultipartFile file = new MockMultipartFile("file", fileContent.getBytes());

        mockMvc.perform(multipart("/uploadLinks")
                .file(file)
                .param("folder", "path/to/myfolder")
                .sessionAttr("user_data", user))
                .andDo(print())
                .andExpect(status().isBadRequest());
    }

    private MockMultipartFile getListOfLinksMockMultipartFile() throws Exception {
        return new MockMultipartFile("file", UploadControllerTest.class.getClassLoader().getResourceAsStream("list-of-links.txt"));
    }
+3 −3
Original line number Diff line number Diff line
http://archives.ia2.inaf.it/files/aao/SC182159.fits.gz
http://archives.ia2.inaf.it/files/aao/SC182160.fits.gz
http://archives.ia2.inaf.it/files/aao/SC182161.fits.gz
http://archives.ia2.inaf.it/files/aao/SC182160.fits.gz/
http://archives.ia2.inaf.it/files/aao/SC182161.fits.gz?query=xxx
http://archives.ia2.inaf.it/files/aao/SC182169.fits.gz  
http://archives.ia2.inaf.it/files/aao/SC182170.fits.gz
http://archives.ia2.inaf.it/files/aao/SC182171.fits.gz
+4 −1
Original line number Diff line number Diff line
@@ -24,7 +24,10 @@
    <b-form-invalid-feedback id="node-name-input-feedback" class="text-right">{{nodeNameError}}</b-form-invalid-feedback>
  </b-form>
  <b-form inline v-if="mode === 'multiple'">
    <p>Upload list of links (separated by newlines)</p>
    <p>
      Upload list of links (separated by newlines)<br />
      <em>Maximum 1000 links per file!</em>
    </p>
    <b-form-file class="text-left" v-model="file" :multiple="false" placeholder="Choose your file or drop it here..." drop-placeholder="Drop file here..." :state="fileState"></b-form-file>
    <b-form-invalid-feedback id="file-input-feedback" class="text-right">{{fileError}}</b-form-invalid-feedback>
  </b-form>