Skip to content
NodeUtils.java 7.47 KiB
Newer Older
package it.inaf.oats.vospace.datamodel;

import net.ivoa.xml.vospace.v2.Node;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
Sara Bertocco's avatar
Sara Bertocco committed
import net.ivoa.xml.vospace.v2.ContainerNode;
import net.ivoa.xml.vospace.v2.DataNode;
import net.ivoa.xml.vospace.v2.StructuredDataNode;
    /**
     * Forbidden path chars are non printable characters and some symbols that
     * could create issues to scripts that manipulates files. Other UTF-8
     * characters are allowed. Front end needs to pay attention to other allowed
     * characters like & and parenthesis in any case, also to avoid XSS attacks.
     */
    private static final Pattern FORBIDDEN_CHARS = Pattern.compile("[\\x00\\x08\\x0B\\x0C\\x0E-\\x1F" + Pattern.quote("<>?\":\\|/'`*") + "]");
    /**
     * Slash is a special character in defining REST endpoints and trying to
     * define a PathVariable containing slashes doesn't work, so the endpoint
     * has been defined using "/nodes/**" instead of "/nodes/{path}" and the
     * path is extracted manually parsing the request URL. Proper URL encoding
     * handling is needed, considering also that slashes mustn't be escaped.
    public static String getPathFromRequestURLString(String requestUrlString) {
        return getPathFromRequestURLString(requestUrlString, "/nodes/");
    }
    public static String getPathFromRequestURLString(String requestUrlString, String prefix) {

        String[] split = requestUrlString.split(prefix);

        String path = "/";
        if (split.length == 2) {
            String[] parts = split[1].split("/");
            path += String.join("/", Arrays.stream(parts)
                    .map(p -> {
                        String decoded = URLDecoder.decode(p, StandardCharsets.UTF_8);
                        if (FORBIDDEN_CHARS.matcher(decoded).find()) {
                            throw new IllegalArgumentException("Path segment " + decoded + " contains an illegal character");
                        }
                        return decoded;
                    })
                    .collect(Collectors.toList()));
    public static String urlEncodePath(String path) {
        String[] parts = path.split("/");
        return String.join("/", Arrays.stream(parts)
                .map(p -> URLEncoder.encode(p, StandardCharsets.UTF_8).replace("+", "%20"))
                .collect(Collectors.toList()));
    }
    // 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
    public static String getParentPath(String path) {

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

        if (parsedPath.length < 2 || !parsedPath[0].isEmpty()) {
            throw new IllegalArgumentException();
        }

        StringBuilder sb = new StringBuilder();
        sb.append("/");

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

        return sb.toString();
    }
    public static List<String> subPathComponents(String path) {
        List resultList = new ArrayList<String>();
        String[] pathComponents = path.split("[/]+");

        if (pathComponents.length == 0) {

            // Manage root node
            resultList.add("/");
            // Manage all precursors in full path
            String parentPath = "/";
            for (int i = 1; i < pathComponents.length; i++) {
                parentPath = parentPath + pathComponents[i] + "/";
                // "I'm managing path = " + parentPath.substring(0, parentPath.length()-1));
                resultList.add(parentPath.substring(0, parentPath.length() - 1));

    public static boolean checkIfWritable(Node myNode, String userName, List<String> userGroups) {
Sara Bertocco's avatar
Sara Bertocco committed
        return checkAccessPropery(myNode, userName, userGroups, NodeProperties.GROUP_WRITE_URI);
Sara Bertocco's avatar
Sara Bertocco committed
    }
    public static boolean checkIfReadable(Node myNode, String userName, List<String> userGroups) {
Sonia Zorba's avatar
Sonia Zorba committed
        if (Boolean.parseBoolean(NodeProperties.getNodePropertyByURI(myNode, NodeProperties.PUBLIC_READ_URI))) {
            return true;
        }

Sara Bertocco's avatar
Sara Bertocco committed
        return checkAccessPropery(myNode, userName, userGroups, NodeProperties.GROUP_READ_URI);
Sara Bertocco's avatar
Sara Bertocco committed
    }

    public static boolean checkAccessPropery(Node myNode, String userName,
            List<String> userGroups, String accessPropertyName) {

        // First check if parent node creator is == userid
                = NodeProperties.getNodePropertyAsListByURI(myNode, NodeProperties.CREATOR_URI);

        if (nodeOwner == null
                || nodeOwner.isEmpty()
                || !nodeOwner.get(0).equals(userName)) {
            // Node owner check has failed: let's check if user can write
            // due to group privileges

            // If the user doesn't belong to any groups throw exception
            if (userGroups == null || userGroups.isEmpty()) {
                return false;
            }

Sara Bertocco's avatar
Sara Bertocco committed
            List<String> groupAccessPropValues
                    = NodeProperties.getNodePropertyAsListByURI(myNode,
Sara Bertocco's avatar
Sara Bertocco committed
                            accessPropertyName);

            // If groupwrite property is absent in Parent Node throw exception
Sara Bertocco's avatar
Sara Bertocco committed
            if (groupAccessPropValues == null
                    || groupAccessPropValues.isEmpty()) {
                return false;
Sara Bertocco's avatar
Sara Bertocco committed
                    = NodeProperties.parsePropertyStringToList(groupAccessPropValues.get(0));

            if (nodeGroups.isEmpty()
                    || !nodeGroups.stream()
                            .anyMatch((i) -> userGroups.contains(i))) {
                return false;
            }

        }

        return true;
Sara Bertocco's avatar
Sara Bertocco committed
    
        
        

    public static String getDbNodeType(Node node) {
        if (node instanceof ContainerNode) {
            return "container";
        } else if (node instanceof DataNode) {
            return "data";
        }
        throw new UnsupportedOperationException("Unable to retrieve database node type for class " + node.getClass().getCanonicalName());
    }
     

    public static String getNodeName(String path) {
        String[] parsedPath = path.split("/");

        return parsedPath[parsedPath.length - 1];
    }

    
    public static String getNodeName(Node myNode) {
        String uri = myNode.getUri();
        return getNodeName(uri);
    }
        

    public static boolean getIsBusy(Node myNode) {

        if (myNode instanceof DataNode) {

            DataNode dataNode = (DataNode) myNode;
            return dataNode.isBusy();
        }

        return false;
    }
    
    
    public static Node getTypedNode(String type) {
        Node node;
        switch (type) {
            case "container":
                node = new ContainerNode();
                break;
            case "data":
                node = new DataNode();
                break;
            case "structured":
                node = new StructuredDataNode();
                break;
            default:
                throw new UnsupportedOperationException("Node type " + type + " not supported yet");
        }
        return node;
    }