package it.inaf.oats.vospace.datamodel; import java.net.URLDecoder; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.regex.Pattern; import java.util.stream.Collectors; import net.ivoa.xml.vospace.v2.Node; public class NodeUtils { /** * 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())); } return path; } 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 subPathComponents(String path) { List resultList = new ArrayList(); String[] pathComponents = path.split("[/]+"); if (pathComponents.length == 0) { // Manage root node resultList.add("/"); } else { // 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)); } } return resultList; } public static boolean checkIfReadable(Node myNode, String userName, List userGroups) { if ("true".equals(NodeProperties.getNodePropertyByURI(myNode, NodeProperties.PUBLIC_READ_URI))) { return true; } String creator = NodeProperties.getNodePropertyByURI(myNode, NodeProperties.CREATOR_URI); if (creator != null && creator.equals(userName)) { return true; } if (userGroups == null || userGroups.isEmpty()) { return false; } List groupReadValues = NodeProperties.getNodePropertyAsListByURI(myNode, NodeProperties.GROUP_READ_URI); if (groupReadValues == null) { return false; } for (String group : groupReadValues) { if (userGroups.contains(group)) { return true; } } return false; } public static boolean checkIfWritable(Node myNode, String userName, List userGroups) { // First check if parent node creator is == userid List nodeOwner = 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; } List groupWritePropValues = NodeProperties.getNodePropertyAsListByURI(myNode, NodeProperties.GROUP_WRITE_URI); // If groupwrite property is absent in Parent Node throw exception if (groupWritePropValues == null || groupWritePropValues.isEmpty()) { return false; } List nodeGroups = NodeProperties.parsePropertyStringToList(groupWritePropValues.get(0)); if (nodeGroups.isEmpty() || !nodeGroups.stream() .anyMatch((i) -> userGroups.contains(i))) { return false; } } return true; } }