Loading src/main/java/it/inaf/oats/vospace/SetNodeController.java +18 −8 Original line number Diff line number Diff line Loading @@ -10,9 +10,12 @@ import it.inaf.oats.vospace.persistence.NodeDAO; import java.util.List; import javax.servlet.http.HttpServletRequest; import net.ivoa.xml.vospace.v2.Node; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; Loading @@ -20,6 +23,8 @@ import org.springframework.web.bind.annotation.RestController; @RestController public class SetNodeController extends BaseNodeController { private static final Logger LOG = LoggerFactory.getLogger(SetNodeController.class); @Autowired private NodeDAO nodeDao; Loading @@ -29,9 +34,10 @@ public class SetNodeController extends BaseNodeController { @PostMapping(value = {"/nodes", "/nodes/**"}, consumes = {MediaType.APPLICATION_XML_VALUE, MediaType.TEXT_XML_VALUE, MediaType.APPLICATION_JSON_VALUE}, produces = {MediaType.APPLICATION_XML_VALUE, MediaType.TEXT_XML_VALUE, MediaType.APPLICATION_JSON_VALUE}) public Node setNode(@RequestBody Node node, User principal, HttpServletRequest request) { public ResponseEntity<Node> setNode(@RequestBody Node node, User principal, HttpServletRequest request) { String path = getPath(); LOG.debug("setNode called for path {}", path); //The service SHALL throw a HTTP 404 status code including a NodeNotFound //fault in the entity-body if the target Node does not exist Loading @@ -48,10 +54,15 @@ public class SetNodeController extends BaseNodeController { // The service SHALL throw a HTTP 403 status code including a PermissionDenied fault // in the entity-body if the request attempts to modify a read-only Property. // This method cannot be used to modify the Node type. String storedNodeType = toBeModifiedNode.getType(); String newNodeType = node.getType(); if(!storedNodeType.equals(newNodeType)) { LOG.debug("setNode trying to modify type. Stored ", storedNodeType + ", requested " + newNodeType); throw new PermissionDeniedException(path); } // This method cannot be used to modify the accepts or provides list of Views for the Node. // This method cannot be used to create children of a container Node. // For this case, throws exception in NodeDAO // This method cannot be used to create children of a container Node. (Non capisco, Sara) // If a parent node in the URI path does not exist then the service SHALL throw a // HTTP 404 status code including a ContainerNotFound fault in the entity-body Loading Loading @@ -79,16 +90,15 @@ public class SetNodeController extends BaseNodeController { } //The service SHOULD throw a HTTP 500 status code including an InternalFault fault // in the entity-body if the operation fails // Done in NodeDAO // to be fixed // A HTTP 200 status code and a Node representation in the entity-body The full // expanded record for the node SHALL be returned, including any xsi:type // specific extensions. return node; return ResponseEntity.ok(nodeDao.setNode(node)); } Loading src/main/java/it/inaf/oats/vospace/persistence/NodeDAO.java +209 −13 Original line number Diff line number Diff line package it.inaf.oats.vospace.persistence; import it.inaf.oats.vospace.DeleteNodeController; import it.inaf.oats.vospace.datamodel.NodeProperties; import it.inaf.oats.vospace.datamodel.NodeUtils; import it.inaf.oats.vospace.datamodel.NodeUtils; import it.inaf.oats.vospace.exception.InternalFaultException; import it.inaf.oats.vospace.exception.NodeNotFoundException; import it.inaf.oats.vospace.exception.PermissionDeniedException; import java.sql.Array; import net.ivoa.xml.vospace.v2.Node; import java.sql.PreparedStatement; Loading @@ -22,6 +25,8 @@ import net.ivoa.xml.vospace.v2.DataNode; import net.ivoa.xml.vospace.v2.Property; import net.ivoa.xml.vospace.v2.StructuredDataNode; import net.ivoa.xml.vospace.v2.View; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.jdbc.core.JdbcTemplate; Loading @@ -30,6 +35,8 @@ import org.springframework.stereotype.Repository; @Repository public class NodeDAO { private static final Logger LOG = LoggerFactory.getLogger(DeleteNodeController.class); @Value("${vospace-authority}") private String authority; Loading @@ -46,17 +53,7 @@ public class NodeDAO { String path = nodeURI.replaceAll("vos://[^/]+", ""); String parentPath = NodeUtils.getParentPath(path); String sql = "SELECT path, relative_path from " + "node n join node_vos_path p on n.node_id = p.node_id " + "where p.vos_path = ?"; List<NodePaths> paths = jdbcTemplate.query(conn -> { PreparedStatement ps = conn.prepareStatement(sql); ps.setString(1, parentPath); return ps; }, (row, index) -> { return getPathsFromResultSet(row); }); List<NodePaths> paths = getNodePathsFromDB(nodeURI); if (paths.isEmpty()) { throw new IllegalStateException("Unable to find parent node during node creation"); Loading Loading @@ -126,6 +123,101 @@ public class NodeDAO { return Optional.of(node); } public Node setNode(Node newNode) { // Verify that the node is in the database String nodeURI = newNode.getUri(); List<NodePaths> paths = getNodePathsFromDB(nodeURI); if (paths.isEmpty()) { throw new IllegalStateException("Unable to find node during node update"); } if (paths.size() > 1) { throw new IllegalStateException("Multiple ltree parent paths during node update"); } // This method cannot be used to modify the accepts or provides list of Views for the Node. // Only DataNodes has Views (see VOSpace Data Model // Check if trying to change views getting Views (if present) of the modified node if (newNode instanceof DataNode) { DataNode dataNode = (DataNode)newNode; List<View> requestedAcceptedViews = dataNode.getAccepts(); List<View> savedAcceptedViews = getAcceptedViewsFromDB(paths.get(0).getPath()); // Get the Views of the saved node List<View> requestedProvidedViews = dataNode.getProvides(); List<View> savedProvidedViews = getProvidedViewsFromDB(paths.get(0).getPath()); if (!requestedAcceptedViews.isEmpty()) { //se sono non nulle, devo fare i controlli, altrimenti di sicuro l'utente non sta chiedendo di cambiarle // Check if requestedAcceptedViews are different from the already stored if (!checkIfViewsAreEquals(requestedAcceptedViews,savedAcceptedViews)) { LOG.debug("setNode trying to modify accepted Views."); throw new PermissionDeniedException("Trying to modify accepted views"); } } if (!requestedProvidedViews.isEmpty()) { //se sono non nulle, devo fare i controlli, altrimenti di sicuro l'utente non sta chiedendo di cambiarle // Check if requestedAcceptedViews are different from the already stored if (!checkIfViewsAreEquals(requestedProvidedViews,savedProvidedViews)) { LOG.debug("setNode trying to modify provided Views."); throw new PermissionDeniedException("Trying to modify provided views"); } } } StringBuilder sb = new StringBuilder(); sb.append("UPDATE node"); sb.append(" SET owner_id = ?, group_read = ?, group_write = ?, is_public = ? " ); sb.append(" FROM node_vos_path p WHERE p.vos_path = ? AND p.node_id = node.node_id "); jdbcTemplate.update(conn -> { PreparedStatement ps = conn.prepareStatement(sb.toString()); int i = 0; ps.setString(++i, NodeProperties.getPropertiesStringByURI(newNode, NodeProperties.getPropertyURI("creator"))); ps.setArray(++i, fromPropertyToArray(ps, NodeProperties.getPropertiesStringByURI(newNode, NodeProperties.getPropertyURI("groupread")))); ps.setArray(++i, fromPropertyToArray(ps, NodeProperties.getPropertiesStringByURI(newNode, NodeProperties.getPropertyURI("groupwrite")))); ps.setBoolean(++i, Boolean.valueOf(NodeProperties.getPropertiesStringByURI(newNode, NodeProperties.getPropertyURI("publicread")))); ps.setString(++i, paths.get(0).getPath()); return ps; }); return newNode; } private Optional<Node> getNode(String path) { String sql = "SELECT node_id, type, async_trans, sticky, busy_state, creator_id, group_read, group_write,\n" + "is_public, content_length, created_on, last_modified, accept_views, provide_views \n" + "FROM node WHERE path = ?"; List<Node> nodes = jdbcTemplate.query(conn -> { PreparedStatement ps = conn.prepareStatement(sql); ps.setString(1, path); return ps; }, (row, index) -> { return getNodeFromResultSet(row); }); if (nodes.isEmpty()) { return Optional.empty(); } // Query returns the node (can be container or data) Node node = nodes.get(0); return Optional.of(node); } private String getFirstLevelChildrenSelector(String path) { String select = "(SELECT path FROM node WHERE node_id = (SELECT node_id FROM node_vos_path WHERE vos_path = ?))::varchar || '"; Loading Loading @@ -322,10 +414,11 @@ public class NodeDAO { return null; } private List<View> getViews(Array array) throws SQLException { private List<View> getViews(Array array) { if (array == null) { return null; } try { return Arrays.stream((String[]) array.getArray()) .map(uri -> { View view = new View(); Loading @@ -333,6 +426,100 @@ public class NodeDAO { return view; }) .collect(Collectors.toList()); } catch (SQLException ex) { throw new RuntimeException(ex); } } private boolean checkIfViewsAreEquals(List<View> viewsA, List<View> viewsB) { boolean viewsAreEqual = true; if ((viewsA == null || viewsA.isEmpty()) && (viewsB == null || viewsB.isEmpty())) return true; if ((viewsA == null || viewsA.isEmpty()) || (viewsB == null || viewsB.isEmpty())) return false; if( viewsA.size() != viewsB.size()) return false; for(int i = 0; i < viewsA.size(); i++) { String viewUriA = viewsA.get(i).getUri(); boolean found = false; for(int j = 0; j < viewsB.size(); j++) { String viewUriB = viewsB.get(i).getUri(); if (viewUriA.compareTo(viewUriB) == 0) { found = true; } } if(found == false) { viewsAreEqual = false; break; } } return viewsAreEqual; } private List<View> getAcceptedViewsFromDB(String nodePath) { return getViewsFromDB(nodePath, "accept_views"); } private List<View> getProvidedViewsFromDB(String nodePath) { return getViewsFromDB(nodePath, "provide_views"); } private List<View> getViewsFromDB(String nodePath, String viewType) { String sql = "SELECT accept_views FROM node WHERE node_id = " + "(SELECT node_id FROM node_vos_path WHERE vos_path = ?)"; Array results = jdbcTemplate.query(sql, ps -> { ps.setString(1, nodePath); }, (rs) -> { if(rs.next()) return rs.getArray("accept_views"); else return null; }); return getViews(results); } private List<NodePaths> getNodePathsFromDB(String nodeURI) { //String nodeURI = myNode.getUri(); String path = nodeURI.replaceAll("vos://[^/]+", ""); String parentPath = NodeUtils.getParentPath(path); String sql = "SELECT path, relative_path from " + "node n join node_vos_path p on n.node_id = p.node_id " + "where p.vos_path = ?"; List<NodePaths> paths = jdbcTemplate.query(conn -> { PreparedStatement ps = conn.prepareStatement(sql); ps.setString(1, parentPath); return ps; }, (row, index) -> { return getPathsFromResultSet(row); }); return paths; } private class NodePaths { Loading @@ -350,5 +537,14 @@ public class NodeDAO { public String toString() { return relativePath + " " + path; } public String getPath() { return this.path; } public String getRelativePath() { return this.relativePath; } } } Loading
src/main/java/it/inaf/oats/vospace/SetNodeController.java +18 −8 Original line number Diff line number Diff line Loading @@ -10,9 +10,12 @@ import it.inaf.oats.vospace.persistence.NodeDAO; import java.util.List; import javax.servlet.http.HttpServletRequest; import net.ivoa.xml.vospace.v2.Node; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; Loading @@ -20,6 +23,8 @@ import org.springframework.web.bind.annotation.RestController; @RestController public class SetNodeController extends BaseNodeController { private static final Logger LOG = LoggerFactory.getLogger(SetNodeController.class); @Autowired private NodeDAO nodeDao; Loading @@ -29,9 +34,10 @@ public class SetNodeController extends BaseNodeController { @PostMapping(value = {"/nodes", "/nodes/**"}, consumes = {MediaType.APPLICATION_XML_VALUE, MediaType.TEXT_XML_VALUE, MediaType.APPLICATION_JSON_VALUE}, produces = {MediaType.APPLICATION_XML_VALUE, MediaType.TEXT_XML_VALUE, MediaType.APPLICATION_JSON_VALUE}) public Node setNode(@RequestBody Node node, User principal, HttpServletRequest request) { public ResponseEntity<Node> setNode(@RequestBody Node node, User principal, HttpServletRequest request) { String path = getPath(); LOG.debug("setNode called for path {}", path); //The service SHALL throw a HTTP 404 status code including a NodeNotFound //fault in the entity-body if the target Node does not exist Loading @@ -48,10 +54,15 @@ public class SetNodeController extends BaseNodeController { // The service SHALL throw a HTTP 403 status code including a PermissionDenied fault // in the entity-body if the request attempts to modify a read-only Property. // This method cannot be used to modify the Node type. String storedNodeType = toBeModifiedNode.getType(); String newNodeType = node.getType(); if(!storedNodeType.equals(newNodeType)) { LOG.debug("setNode trying to modify type. Stored ", storedNodeType + ", requested " + newNodeType); throw new PermissionDeniedException(path); } // This method cannot be used to modify the accepts or provides list of Views for the Node. // This method cannot be used to create children of a container Node. // For this case, throws exception in NodeDAO // This method cannot be used to create children of a container Node. (Non capisco, Sara) // If a parent node in the URI path does not exist then the service SHALL throw a // HTTP 404 status code including a ContainerNotFound fault in the entity-body Loading Loading @@ -79,16 +90,15 @@ public class SetNodeController extends BaseNodeController { } //The service SHOULD throw a HTTP 500 status code including an InternalFault fault // in the entity-body if the operation fails // Done in NodeDAO // to be fixed // A HTTP 200 status code and a Node representation in the entity-body The full // expanded record for the node SHALL be returned, including any xsi:type // specific extensions. return node; return ResponseEntity.ok(nodeDao.setNode(node)); } Loading
src/main/java/it/inaf/oats/vospace/persistence/NodeDAO.java +209 −13 Original line number Diff line number Diff line package it.inaf.oats.vospace.persistence; import it.inaf.oats.vospace.DeleteNodeController; import it.inaf.oats.vospace.datamodel.NodeProperties; import it.inaf.oats.vospace.datamodel.NodeUtils; import it.inaf.oats.vospace.datamodel.NodeUtils; import it.inaf.oats.vospace.exception.InternalFaultException; import it.inaf.oats.vospace.exception.NodeNotFoundException; import it.inaf.oats.vospace.exception.PermissionDeniedException; import java.sql.Array; import net.ivoa.xml.vospace.v2.Node; import java.sql.PreparedStatement; Loading @@ -22,6 +25,8 @@ import net.ivoa.xml.vospace.v2.DataNode; import net.ivoa.xml.vospace.v2.Property; import net.ivoa.xml.vospace.v2.StructuredDataNode; import net.ivoa.xml.vospace.v2.View; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.jdbc.core.JdbcTemplate; Loading @@ -30,6 +35,8 @@ import org.springframework.stereotype.Repository; @Repository public class NodeDAO { private static final Logger LOG = LoggerFactory.getLogger(DeleteNodeController.class); @Value("${vospace-authority}") private String authority; Loading @@ -46,17 +53,7 @@ public class NodeDAO { String path = nodeURI.replaceAll("vos://[^/]+", ""); String parentPath = NodeUtils.getParentPath(path); String sql = "SELECT path, relative_path from " + "node n join node_vos_path p on n.node_id = p.node_id " + "where p.vos_path = ?"; List<NodePaths> paths = jdbcTemplate.query(conn -> { PreparedStatement ps = conn.prepareStatement(sql); ps.setString(1, parentPath); return ps; }, (row, index) -> { return getPathsFromResultSet(row); }); List<NodePaths> paths = getNodePathsFromDB(nodeURI); if (paths.isEmpty()) { throw new IllegalStateException("Unable to find parent node during node creation"); Loading Loading @@ -126,6 +123,101 @@ public class NodeDAO { return Optional.of(node); } public Node setNode(Node newNode) { // Verify that the node is in the database String nodeURI = newNode.getUri(); List<NodePaths> paths = getNodePathsFromDB(nodeURI); if (paths.isEmpty()) { throw new IllegalStateException("Unable to find node during node update"); } if (paths.size() > 1) { throw new IllegalStateException("Multiple ltree parent paths during node update"); } // This method cannot be used to modify the accepts or provides list of Views for the Node. // Only DataNodes has Views (see VOSpace Data Model // Check if trying to change views getting Views (if present) of the modified node if (newNode instanceof DataNode) { DataNode dataNode = (DataNode)newNode; List<View> requestedAcceptedViews = dataNode.getAccepts(); List<View> savedAcceptedViews = getAcceptedViewsFromDB(paths.get(0).getPath()); // Get the Views of the saved node List<View> requestedProvidedViews = dataNode.getProvides(); List<View> savedProvidedViews = getProvidedViewsFromDB(paths.get(0).getPath()); if (!requestedAcceptedViews.isEmpty()) { //se sono non nulle, devo fare i controlli, altrimenti di sicuro l'utente non sta chiedendo di cambiarle // Check if requestedAcceptedViews are different from the already stored if (!checkIfViewsAreEquals(requestedAcceptedViews,savedAcceptedViews)) { LOG.debug("setNode trying to modify accepted Views."); throw new PermissionDeniedException("Trying to modify accepted views"); } } if (!requestedProvidedViews.isEmpty()) { //se sono non nulle, devo fare i controlli, altrimenti di sicuro l'utente non sta chiedendo di cambiarle // Check if requestedAcceptedViews are different from the already stored if (!checkIfViewsAreEquals(requestedProvidedViews,savedProvidedViews)) { LOG.debug("setNode trying to modify provided Views."); throw new PermissionDeniedException("Trying to modify provided views"); } } } StringBuilder sb = new StringBuilder(); sb.append("UPDATE node"); sb.append(" SET owner_id = ?, group_read = ?, group_write = ?, is_public = ? " ); sb.append(" FROM node_vos_path p WHERE p.vos_path = ? AND p.node_id = node.node_id "); jdbcTemplate.update(conn -> { PreparedStatement ps = conn.prepareStatement(sb.toString()); int i = 0; ps.setString(++i, NodeProperties.getPropertiesStringByURI(newNode, NodeProperties.getPropertyURI("creator"))); ps.setArray(++i, fromPropertyToArray(ps, NodeProperties.getPropertiesStringByURI(newNode, NodeProperties.getPropertyURI("groupread")))); ps.setArray(++i, fromPropertyToArray(ps, NodeProperties.getPropertiesStringByURI(newNode, NodeProperties.getPropertyURI("groupwrite")))); ps.setBoolean(++i, Boolean.valueOf(NodeProperties.getPropertiesStringByURI(newNode, NodeProperties.getPropertyURI("publicread")))); ps.setString(++i, paths.get(0).getPath()); return ps; }); return newNode; } private Optional<Node> getNode(String path) { String sql = "SELECT node_id, type, async_trans, sticky, busy_state, creator_id, group_read, group_write,\n" + "is_public, content_length, created_on, last_modified, accept_views, provide_views \n" + "FROM node WHERE path = ?"; List<Node> nodes = jdbcTemplate.query(conn -> { PreparedStatement ps = conn.prepareStatement(sql); ps.setString(1, path); return ps; }, (row, index) -> { return getNodeFromResultSet(row); }); if (nodes.isEmpty()) { return Optional.empty(); } // Query returns the node (can be container or data) Node node = nodes.get(0); return Optional.of(node); } private String getFirstLevelChildrenSelector(String path) { String select = "(SELECT path FROM node WHERE node_id = (SELECT node_id FROM node_vos_path WHERE vos_path = ?))::varchar || '"; Loading Loading @@ -322,10 +414,11 @@ public class NodeDAO { return null; } private List<View> getViews(Array array) throws SQLException { private List<View> getViews(Array array) { if (array == null) { return null; } try { return Arrays.stream((String[]) array.getArray()) .map(uri -> { View view = new View(); Loading @@ -333,6 +426,100 @@ public class NodeDAO { return view; }) .collect(Collectors.toList()); } catch (SQLException ex) { throw new RuntimeException(ex); } } private boolean checkIfViewsAreEquals(List<View> viewsA, List<View> viewsB) { boolean viewsAreEqual = true; if ((viewsA == null || viewsA.isEmpty()) && (viewsB == null || viewsB.isEmpty())) return true; if ((viewsA == null || viewsA.isEmpty()) || (viewsB == null || viewsB.isEmpty())) return false; if( viewsA.size() != viewsB.size()) return false; for(int i = 0; i < viewsA.size(); i++) { String viewUriA = viewsA.get(i).getUri(); boolean found = false; for(int j = 0; j < viewsB.size(); j++) { String viewUriB = viewsB.get(i).getUri(); if (viewUriA.compareTo(viewUriB) == 0) { found = true; } } if(found == false) { viewsAreEqual = false; break; } } return viewsAreEqual; } private List<View> getAcceptedViewsFromDB(String nodePath) { return getViewsFromDB(nodePath, "accept_views"); } private List<View> getProvidedViewsFromDB(String nodePath) { return getViewsFromDB(nodePath, "provide_views"); } private List<View> getViewsFromDB(String nodePath, String viewType) { String sql = "SELECT accept_views FROM node WHERE node_id = " + "(SELECT node_id FROM node_vos_path WHERE vos_path = ?)"; Array results = jdbcTemplate.query(sql, ps -> { ps.setString(1, nodePath); }, (rs) -> { if(rs.next()) return rs.getArray("accept_views"); else return null; }); return getViews(results); } private List<NodePaths> getNodePathsFromDB(String nodeURI) { //String nodeURI = myNode.getUri(); String path = nodeURI.replaceAll("vos://[^/]+", ""); String parentPath = NodeUtils.getParentPath(path); String sql = "SELECT path, relative_path from " + "node n join node_vos_path p on n.node_id = p.node_id " + "where p.vos_path = ?"; List<NodePaths> paths = jdbcTemplate.query(conn -> { PreparedStatement ps = conn.prepareStatement(sql); ps.setString(1, parentPath); return ps; }, (row, index) -> { return getPathsFromResultSet(row); }); return paths; } private class NodePaths { Loading @@ -350,5 +537,14 @@ public class NodeDAO { public String toString() { return relativePath + " " + path; } public String getPath() { return this.path; } public String getRelativePath() { return this.relativePath; } } }