Skip to content
CreateNodeController.java 6.23 KiB
Newer Older
Sara Bertocco's avatar
Sara Bertocco committed
package it.inaf.oats.vospace;

Sara Bertocco's avatar
Sara Bertocco committed
import net.ivoa.xml.vospace.v2.Node;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.RequestBody;
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;
Sara Bertocco's avatar
Sara Bertocco committed

@RestController
public class CreateNodeController extends BaseNodeController {
Sara Bertocco's avatar
Sara Bertocco committed

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

Sara Bertocco's avatar
Sara Bertocco committed
            consumes = {MediaType.APPLICATION_XML_VALUE, MediaType.APPLICATION_JSON_VALUE},
            produces = {MediaType.APPLICATION_XML_VALUE, MediaType.APPLICATION_JSON_VALUE})
    public Node createNode(@RequestBody Node node, User principal) {

        String path = getPath();
Sara Bertocco's avatar
Sara Bertocco committed

            throw new InvalidURIException(node.getUri());
        // Check if payload URI is consistent with http request
        if (!isUrlConsistentWithPayloadURI(node.getUri(), path)) {
            throw new InvalidURIException(node.getUri(), path);
        }

        // Check if another node is already present at specified 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)));

        // Check if parent node is not a Container node and in case throw
        // appropriate exception
        if (!parentNode.getType().equals("vos:ContainerNode")) {
            if (parentNode.getType().equals("vos:LinkNode")) {
                throw new LinkFoundException(getParentPath(path));
            } else {
                throw new ContainerNotFoundException(getParentPath(path));
            }
        // First check if parent node creator is == userid
        List<String> nodeOwner
                = getNodePropertyByURI(
                        parentNode, "ivo://ivoa.net/vospace/core#creator");

        if (nodeOwner == null
                || nodeOwner.isEmpty()
                || !nodeOwner.get(0).equals(principal.getName())) {
            // Node owner check has failed: let's check if user can write
            // due to group privileges
            List<String> userGroups = principal.getGroups();

            // If the user doesn't belong to any groups throw exception
            if (userGroups == null || userGroups.isEmpty()) {
                throw new PermissionDeniedException(path);
            List<String> groupWritePropValues
                    = getNodePropertyByURI(parentNode,
                            "ivo://ivoa.net/vospace/core#groupwrite");

            // If groupwrite property is absent in Parent Node throw exception
            if (groupWritePropValues == null
                    || groupWritePropValues.isEmpty()) {
                throw new PermissionDeniedException(path);
            }

            List<String> nodeGroups
                    = parsePropertyStringToList(groupWritePropValues.get(0));

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

        }

        nodeDao.createNode(node);

Sara Bertocco's avatar
Sara Bertocco committed
    }
    // Assuming that this service implementation uses only ! as a separator
    // in the authority part of the URI
    private boolean isValidURI(String nodeURI) {
            parsedAuthority = nodeURI.replaceAll("vos://", "").split("/", -1)[0];

        if (parsedAuthority.isEmpty()
                || !parsedAuthority.replace("~", "!").equals(authority)) {
    }

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

    }

    // This method assumes that URL is in the format /node1/node2/...
    // multiple slashes as a single separator are allowed
    // But the output has only single slash separators
    private String getParentPath(String path) {

        String[] parsedPath = path.split("[/]+");

        if (parsedPath.length < 2 || !parsedPath[0].isEmpty()) {
            throw new IllegalArgumentException();
        }
        for (int i = 1; i < parsedPath.length - 1; i++) {
            sb.append(parsedPath[i]);
            if (i < parsedPath.length - 2) {
                sb.append("/");
            }

    // Returns all properties stored inside the node under the requested
    // property URI.    
    private List<String> getNodePropertyByURI(Node node, String propertyURI) {

        List<String> propertyList = node.getProperties().stream()
                .filter((i) -> i.getUri()
                .equals(propertyURI))
                .map((i) -> i.getValue())
                .collect(Collectors.toList());

        return propertyList;
    }

    private List<String> parsePropertyStringToList(String property) {
        // If separator changes, this method should remain consistent
        // For now it assumes that " " is the separator        
        String separator = " ";

        String trimmedProperty = property.trim();
        if (trimmedProperty.isEmpty()) {
            return List.of();
        }

        return List.of(trimmedProperty.split(separator));

    }

Sara Bertocco's avatar
Sara Bertocco committed
}