From 836bd0a7d637fab714910133057403dec1dcf422 Mon Sep 17 00:00:00 2001 From: Nicola Fulvio Calabria <nicola.calabria@inaf.it> Date: Thu, 17 Jun 2021 13:26:30 +0200 Subject: [PATCH] Implemented support to node URI percent encoding/decoding --- .../java/it/inaf/oats/vospace/URIUtils.java | 96 ++++++++++++------- .../oats/vospace/persistence/NodeDAO.java | 43 +++++---- .../it/inaf/oats/vospace/URIUtilsTest.java | 17 +++- 3 files changed, 100 insertions(+), 56 deletions(-) diff --git a/src/main/java/it/inaf/oats/vospace/URIUtils.java b/src/main/java/it/inaf/oats/vospace/URIUtils.java index 0fd86c2..b30df8d 100644 --- a/src/main/java/it/inaf/oats/vospace/URIUtils.java +++ b/src/main/java/it/inaf/oats/vospace/URIUtils.java @@ -9,6 +9,7 @@ import it.inaf.oats.vospace.exception.InvalidURIException; import java.net.URI; import java.net.URISyntaxException; import java.util.regex.Pattern; +import net.ivoa.xml.vospace.v2.Node; public class URIUtils { @@ -17,48 +18,69 @@ public class URIUtils { private static final Pattern FORBIDDEN_CHARS = Pattern.compile("[\\x00\\x08\\x0B\\x0C\\x0E-\\x1F" + Pattern.quote("<>?\":\\|'`*") + "]"); private static final String SCHEME = "vos"; + public static String returnURIFromVosPath(String vosPath, String authority) + throws URISyntaxException { + URI uri = new URI( + SCHEME, + authority, + vosPath, + null, + null + ); + + return uri.toASCIIString(); + } + + public static String returnVosPathFromNodeURI(Node myNode, String authority) { + return returnVosPathFromNodeURI(myNode.getUri(), authority); + } + // This method validates the URI too public static String returnVosPathFromNodeURI(String nodeURI, String authority) { - + String resultPath = null; - - try{ - URI uri = new URI(nodeURI); - - // Check scheme - if(!uri.isAbsolute() || - uri.isOpaque() || - !uri.getRawSchemeSpecificPart().startsWith("//") || - !uri.getScheme().equalsIgnoreCase(SCHEME)) - throw new InvalidURIException(nodeURI); - - // Check authority - if(!uri.getAuthority().replace("~", "!").equals(authority)) - throw new InvalidURIException(nodeURI); - - // Check path - String rawPath = uri.getRawPath(); - - // Check if raw Path is null or contains percent encoded slashes or multiple - // separators - if(rawPath == null || - rawPath.contains("//") || - rawPath.contains("%2F") || - rawPath.contains("%2f")) - throw new InvalidURIException(nodeURI); - - resultPath = uri.getPath(); - - if(resultPath.isBlank() || - FORBIDDEN_CHARS.matcher(resultPath).find() || - (!resultPath.equals("/") && resultPath.endsWith("/"))) - throw new InvalidURIException(nodeURI); - - } catch(URISyntaxException e) { + + try { + URI uri = new URI(nodeURI); + + // Check scheme + if (!uri.isAbsolute() + || uri.isOpaque() + || !uri.getRawSchemeSpecificPart().startsWith("//") + || !uri.getScheme().equalsIgnoreCase(SCHEME)) { + throw new InvalidURIException(nodeURI); + } + + // Check authority + if (!uri.getAuthority().replace("~", "!").equals(authority)) { + throw new InvalidURIException(nodeURI); + } + + // Check path + String rawPath = uri.getRawPath(); + + // Check if raw Path is null or contains percent encoded slashes or multiple + // separators + if (rawPath == null + || rawPath.contains("//") + || rawPath.contains("%2F") + || rawPath.contains("%2f")) { + throw new InvalidURIException(nodeURI); + } + + resultPath = uri.getPath(); + + if (resultPath.isBlank() + || FORBIDDEN_CHARS.matcher(resultPath).find() + || (!resultPath.equals("/") && resultPath.endsWith("/"))) { + throw new InvalidURIException(nodeURI); + } + + } catch (URISyntaxException e) { throw new InvalidURIException(nodeURI); } - - return resultPath; + + return resultPath; } diff --git a/src/main/java/it/inaf/oats/vospace/persistence/NodeDAO.java b/src/main/java/it/inaf/oats/vospace/persistence/NodeDAO.java index f194e02..c6cd9eb 100644 --- a/src/main/java/it/inaf/oats/vospace/persistence/NodeDAO.java +++ b/src/main/java/it/inaf/oats/vospace/persistence/NodeDAO.java @@ -10,6 +10,7 @@ import it.inaf.oats.vospace.URIUtils; import it.inaf.oats.vospace.datamodel.NodeProperties; import it.inaf.oats.vospace.datamodel.NodeUtils; import it.inaf.oats.vospace.exception.InternalFaultException; +import java.net.URISyntaxException; import java.sql.Array; import net.ivoa.xml.vospace.v2.Node; import java.sql.PreparedStatement; @@ -58,7 +59,7 @@ public class NodeDAO { public void createNode(Node myNode, String jobId) { - String nodeVosPath = URIUtils.returnVosPathFromNodeURI(myNode.getUri(), authority); + String nodeVosPath = URIUtils.returnVosPathFromNodeURI(myNode, authority); List<NodePaths> paths = getNodePathsFromDB(nodeVosPath); @@ -144,7 +145,7 @@ public class NodeDAO { */ public Node setNode(Node newNode, boolean recursive) { - String vosPath = NodeUtils.getVosPath(newNode); + String vosPath = URIUtils.returnVosPathFromNodeURI(newNode, authority); if (recursive) { updatePermissionsRecursively(newNode, vosPath); @@ -240,10 +241,10 @@ public class NodeDAO { throw new InternalFaultException("More than 1 node id at path: " + nodeVosPath); } } - + public Optional<ShortNodeDescriptor> getShortNodeDescriptor(String nodeVosPath, String userId, List<String> userGroups) { - + String sql = "SELECT path,\n" + "NOT (n.async_trans OR n.sticky OR COALESCE(location_type = 'async', FALSE)) AS is_writable,\n" + "((SELECT COUNT(*) FROM (SELECT UNNEST(?) INTERSECT SELECT UNNEST(n.group_write)) AS allowed_groups ) = 0 AND\n" @@ -257,15 +258,15 @@ public class NodeDAO { Optional<ShortNodeDescriptor> sndOpt = jdbcTemplate.query(conn -> { PreparedStatement ps = conn.prepareStatement(sql); - + String[] groups; - if(userGroups == null){ - groups = new String[0]; + if (userGroups == null) { + groups = new String[0]; } else { groups = userGroups.toArray(String[]::new); } ps.setArray(1, ps.getConnection().createArrayOf("varchar", groups)); - + ps.setString(2, userId); ps.setString(3, nodeVosPath); return ps; @@ -342,15 +343,15 @@ public class NodeDAO { return jdbcTemplate.query(sql, ps -> { ps.setLong(1, parentNodeId); - + String[] groups; - if(userGroups == null){ - groups = new String[0]; + if (userGroups == null) { + groups = new String[0]; } else { groups = userGroups.toArray(String[]::new); - } + } ps.setArray(2, ps.getConnection().createArrayOf("varchar", groups)); - + ps.setString(3, userId); }, row -> { if (!row.next()) { @@ -454,7 +455,17 @@ public class NodeDAO { } private String getUri(String path) { - return "vos://" + authority + path; + // Percent encode path + String result = null; + + try { + result = URIUtils.returnURIFromVosPath(path, authority); + } catch (URISyntaxException e) { + throw new InternalFaultException("unable to percent encode URI from authority and path: " + + authority + " , " + path); + } + + return result; } private NodePaths getPathsFromResultSet(ResultSet rs) throws SQLException { @@ -583,7 +594,7 @@ public class NodeDAO { } private List<NodePaths> getNodePathsFromDB(String path) { - + String parentPath = NodeUtils.getParentPath(path); String sql = "SELECT path, relative_path " @@ -633,7 +644,7 @@ public class NodeDAO { public boolean isBusy() { return busy; } - + public boolean isPermissionDenied() { return permissionDenied; } diff --git a/src/test/java/it/inaf/oats/vospace/URIUtilsTest.java b/src/test/java/it/inaf/oats/vospace/URIUtilsTest.java index b3e3733..d630f85 100644 --- a/src/test/java/it/inaf/oats/vospace/URIUtilsTest.java +++ b/src/test/java/it/inaf/oats/vospace/URIUtilsTest.java @@ -6,6 +6,7 @@ package it.inaf.oats.vospace; import it.inaf.oats.vospace.exception.InvalidURIException; +import java.net.URISyntaxException; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import org.junit.jupiter.api.Test; @@ -57,7 +58,7 @@ public class URIUtilsTest { assertThrows(InvalidURIException.class, () -> { URIUtils.returnVosPathFromNodeURI(test7, authority); - }); + }); String test8 = "vos://example.com!vospace/n1/n2/n3/n4"; @@ -67,11 +68,21 @@ public class URIUtilsTest { String test9 = "vos://example.com!vospace/"; assertEquals("/", - URIUtils.returnVosPathFromNodeURI(test9, authority)); - + URIUtils.returnVosPathFromNodeURI(test9, authority)); + } + + @Test + public void testReturnURIFromVosPath() throws URISyntaxException + { + String test1 = URIUtils.returnURIFromVosPath("/", authority); + assertEquals("vos://"+authority+"/", test1); + String test2 = URIUtils.returnURIFromVosPath("/test1/test2", authority); + assertEquals("vos://"+authority+"/test1/test2", test2); + String test3 = URIUtils.returnURIFromVosPath("/test1/te# !?st2", authority); + assertEquals("vos://"+authority+"/test1/te%23%20!%3Fst2", test3); } -- GitLab