Commit 2c70d71b authored by Nicola Fulvio Calabria's avatar Nicola Fulvio Calabria
Browse files

Task #3546 - Complete CreateNode endpoint implementation. Added tests:

didn't use new Mock Filter for User injection, will include it later
parent 0358e683
Loading
Loading
Loading
Loading
+84 −0
Original line number Diff line number Diff line
@@ -8,7 +8,11 @@ import org.springframework.web.bind.annotation.RestController;
import org.springframework.beans.factory.annotation.Autowired;
import it.inaf.oats.vospace.persistence.NodeDAO;
import java.util.List;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.PutMapping;
import it.inaf.oats.vospace.exception.*;
import java.util.stream.Collectors;
import java.util.Arrays;

@RestController
public class CreateNodeController extends BaseNodeController {
@@ -16,6 +20,9 @@ public class CreateNodeController extends BaseNodeController {
    @Autowired
    private NodeDAO nodeDao;

    @Value("${vospace-authority}")
    private String authority;

    @PutMapping(value = {"/nodes", "/nodes/**"},
            consumes = {MediaType.APPLICATION_XML_VALUE, MediaType.APPLICATION_JSON_VALUE},
            produces = {MediaType.APPLICATION_XML_VALUE, MediaType.APPLICATION_JSON_VALUE})
@@ -25,7 +32,84 @@ public class CreateNodeController extends BaseNodeController {

        List<String> userGroups = principal.getGroups();

        if (!isValidURI(node.getUri()))
            throw new InvalidURIException(node.getUri());
        
        if(!isUrlConsistentWithPayloadURI(node.getUri(), path)) {
            throw new InvalidURIException(node.getUri(), path);
        }

        // This checks if the user is trying to insert the root node at "/" too
        if (nodeDao.listNode(path).isPresent()) {
            throw new DuplicateNodeException(path);
        }

        // Retrieve parent node 
        Node parentNode = nodeDao.listNode(getParentPath(path))
                .orElseThrow(() -> new ContainerNotFoundException(getParentPath(path)));

        List<String> groupWritePropValues = parentNode.getProperties().stream()
                .filter((i) -> i.getUri()
                .equals("ivo://ivoa.net/vospace/core#groupwrite"))
                .map((i) -> i.getValue())
                .collect(Collectors.toList());

        if (groupWritePropValues.isEmpty()) {
            throw new PermissionDeniedException(path);
        }

        List<String> nodeGroups
                = Arrays.asList(groupWritePropValues.get(0).split(",",-1));

        if (!nodeGroups.stream().anyMatch((i) -> userGroups.contains(i))) {
            throw new PermissionDeniedException(path);
        }

        // Check if parent node is a LinkNode and if so throw exception
        if (parentNode.getType().equals("vos:LinkNode")) {
            throw new LinkFoundException(getParentPath(path));
        }

        nodeDao.createNode(node);
        return node;
    }
    
    // Assuming that this service implementation uses only ! as a separator
    // in the authority part of the URI
    private boolean isValidURI(String nodeURI)
    {
        String parsedAuthority;
        if(!nodeURI.startsWith("vos://"))
        {
            return false;
        } else {
            parsedAuthority = nodeURI.replaceAll("vos://", "").split("/",-1)[0];
        }
        
        if(parsedAuthority.isEmpty() || 
                !parsedAuthority.replace("~","!").equals(authority))
            return false;
        
        return true;        
    }

    private boolean isUrlConsistentWithPayloadURI(String nodeURI, String path) {
        // It's assumed that nodeURI has been validated to be in the expected 
        // form vos://authority[!~]somepath/mynode..."

        return nodeURI.replaceAll("vos://[^/]+", "").equals(path);

    }

    private String getParentPath(String path) {
        String[] parsedPath = path.split("/");

        StringBuilder sb = new StringBuilder();

        for (int i = 0; i < parsedPath.length - 1; i++) {
            sb.append("/").append(parsedPath[i]);
        }

        return sb.toString();
    }
}
+12 −0
Original line number Diff line number Diff line
package it.inaf.oats.vospace.exception;

import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;

@ResponseStatus(value = HttpStatus.NOT_FOUND)
public class ContainerNotFoundException extends VoSpaceException {

    public ContainerNotFoundException(String path) {
        super("Container Not Found at path: " + path);
    }
}
+12 −0
Original line number Diff line number Diff line
package it.inaf.oats.vospace.exception;

import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;

@ResponseStatus(value = HttpStatus.CONFLICT)
public class DuplicateNodeException extends VoSpaceException {

    public DuplicateNodeException(String path) {
        super("Duplicate Node at path: " + path);
    }
}
+18 −0
Original line number Diff line number Diff line
package it.inaf.oats.vospace.exception;

import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;

@ResponseStatus(value = HttpStatus.BAD_REQUEST)
public class InvalidURIException extends VoSpaceException {

    public InvalidURIException(String URI, String path) {
        super("InvalidURI. Payload node URI: " + URI + 
                " is not consistent with request path: " + path);
    }
    
    public InvalidURIException(String URI)
    {
        super("InvalidURI. URI: "+URI+ " is not in a valid format");
    }
}
+12 −0
Original line number Diff line number Diff line
package it.inaf.oats.vospace.exception;

import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;

@ResponseStatus(value = HttpStatus.BAD_REQUEST)
public class LinkFoundException extends VoSpaceException {

    public LinkFoundException(String path) {
        super("Link Found at path: " + path);
    }
}
Loading