Commit 30778de3 authored by Sara Bertocco's avatar Sara Bertocco
Browse files

Finalized task #3549 - Implement deleteNode endpoint

parent 27bd6a94
Loading
Loading
Loading
Loading
+14 −51
Original line number Diff line number Diff line
@@ -20,8 +20,10 @@ import java.util.stream.Collectors;
import javax.servlet.http.HttpServletRequest;
import net.ivoa.xml.vospace.v2.Node;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@@ -32,10 +34,9 @@ public class DeleteNodeController extends BaseNodeController {
    @Autowired
    private NodeDAO nodeDAO;

    @GetMapping(value = {"/nodes", "/nodes/**"},
            consumes = {MediaType.APPLICATION_XML_VALUE, MediaType.TEXT_XML_VALUE, MediaType.APPLICATION_JSON_VALUE},
    @DeleteMapping(value = {"/nodes", "/nodes/**"},
            produces = {MediaType.APPLICATION_XML_VALUE, MediaType.TEXT_XML_VALUE, MediaType.APPLICATION_JSON_VALUE})
    public ResponseEntity<Node> deleteNode(HttpServletRequest request, User principal) {
    public ResponseEntity<String> deleteNode(HttpServletRequest request, User principal) {
        
        String path = getPath();
        
@@ -60,7 +61,7 @@ public class DeleteNodeController extends BaseNodeController {
        } else {
            
            // Manage all precursors in full path
            for (int i = 1; i < pathComponents.size(); i++) { 
            for (int i = 0; i < pathComponents.size(); i++) { 
                String tmpPath = pathComponents.get(i);
                Node mynode = nodeDAO.listNode(tmpPath)
                        .orElseThrow(() -> new NodeNotFoundException(tmpPath));
@@ -71,55 +72,17 @@ public class DeleteNodeController extends BaseNodeController {
                    
        }
        
        // DUPLICATED code from CreateNodeController - BEGIN
        List<String> nodeOwner
                = NodeProperties.getNodePropertyByURI(toBeDeletedNode, NodeProperties.CREATOR_URI);
                //= getNodePropertyByURI(
                //        toBeDeletedNode, "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()) {
        if(!NodeUtils.checkIfWritable(toBeDeletedNode, principal.getName(), principal.getGroups())) {
            throw new PermissionDeniedException(path);
        }
                
            List<String> groupWritePropValues
                    = NodeProperties.getNodePropertyByURI(toBeDeletedNode,
                            NodeProperties.CREATOR_URI);

            // If groupwrite property is absent in Parent Node throw exception
            if (groupWritePropValues == null
                    || groupWritePropValues.isEmpty()) {
                throw new PermissionDeniedException(path);
        try {
            nodeDAO.deleteNode(path);
            return ResponseEntity.ok("Node deleted");
        } catch(Exception ex) {
            return new ResponseEntity<>(ex.getMessage(), HttpStatus.BAD_REQUEST);
        }
        
            List<String> nodeGroups
                    = NodeProperties.parsePropertyStringToList(groupWritePropValues.get(0));

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

        }

        // DUPLICATED code from CreateNodeController - END
        
        
        return ResponseEntity.ok(toBeDeletedNode);
        //return toBeDeletedNode;
        //return ResponseEntity.ok(nodeDAO.deleteNode(path)
        //        .orElseThrow(() -> new InternalFaultException(path)));
        
    }
    
}
+1 −14
Original line number Diff line number Diff line
@@ -23,10 +23,7 @@ import org.springframework.beans.factory.annotation.Value;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

/**
 *
 * @author bertocco
 */

@Repository
public class NodeDAO {

@@ -126,16 +123,6 @@ public class NodeDAO {
        return Optional.of(node);
    }

    public Optional<String> deleteNode(String path) {
        
        // To be filled
        
        // In case of failure return empty object to trigger HTTP 500 
        // status code for InternalFault
        return Optional.of("OK");
        
    }

    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 || '";

+113 −113
Original line number Diff line number Diff line
package it.inaf.oats.vospace;

import static it.inaf.oats.vospace.CreateNodeControllerTest.getResourceFileContent;
import it.inaf.oats.vospace.datamodel.NodeProperties;
import it.inaf.oats.vospace.persistence.NodeDAO;
import java.util.List;
import java.util.Optional;
import net.ivoa.xml.vospace.v2.ContainerNode;
import net.ivoa.xml.vospace.v2.DataNode;
import net.ivoa.xml.vospace.v2.LinkNode;
import net.ivoa.xml.vospace.v2.Node;
import net.ivoa.xml.vospace.v2.Property;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
@@ -17,6 +21,7 @@ import org.springframework.test.web.servlet.MockMvc;
import org.junit.jupiter.api.Test;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.when;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put;
@@ -34,10 +39,6 @@ public class DeleteNodeControllerTest {
    @MockBean
    private NodeDAO nodeDao;

    @SpyBean
    @Autowired
    private DeleteNodeController controller;

    @Autowired
    private MockMvc mockMvc;

@@ -51,157 +52,156 @@ public class DeleteNodeControllerTest {
                        .delete("/nodes")
                        .header("Authorization", "Bearer user2_token"))
                        .andExpect(status().isForbidden());
        /*
        mockMvc.perform(delete("/members/1").
                .header("Authorization", "Bearer user2_token"))
                .andExpect(status().isNotFound());
        mockMvc.perform(delete("/")
                .header("Authorization", "Bearer user2_token"))
                .andExpect(status().isNotFound());
        */
        
    }
   
    
    @Test
    public void testNodeNotFound() throws Exception {
    public void testDeleteFirstLevelNode() throws Exception {
        
        when(nodeDao.listNode(eq("/mynode"))).thenReturn(Optional.of(getDataNode()));
        when(nodeDao.listNode(eq("/mynode"))).thenReturn(Optional.of(getWritableDataNode()));
        
        mockMvc.perform(MockMvcRequestBuilders
                        .delete("/nodes/mynode")
                        .header("Authorization", "Bearer user2_token"))
                        .andExpect(status().isOk());
        /*
        mockMvc.perform(delete("/members/1").
                .header("Authorization", "Bearer user2_token"))
                .andExpect(status().isNotFound());
        mockMvc.perform(delete("/")
                .header("Authorization", "Bearer user2_token"))
                .andExpect(status().isNotFound());
        */
    }
    /*
    @Test
    public void testDeleteRoot1() throws Exception {
        
        mockMvc.perform(put("/nodes/mydata1")
                .header("Authorization", "Bearer user2_token"))
                //.content(requestBody)
                //.contentType(MediaType.APPLICATION_JSON)
                //.accept(MediaType.APPLICATION_XML))
                //.andDo(print())
                .andExpect(status().isOk());

        
    }
       

    
    @Test
    public void testDeleteRoot2() throws Exception {
    public void testDeleteMoreLevelNode() throws Exception {
        
        when(nodeDao.listNode(eq("/mynode"))).thenReturn(Optional.of(getWritableDataNode("/mynode")));        
        when(nodeDao.listNode(eq("/mynode/middlenode"))).thenReturn(Optional.of(getWritableDataNode("/mynode/middlenode")));        
        when(nodeDao.listNode(eq("/mynode/middlenode/myfile.txt"))).thenReturn(Optional.of(getWritableDataNode("/mynode/middlenode/myfile.txt")));
        
        mockMvc.perform(put("/nodes/mydata1")
        mockMvc.perform(MockMvcRequestBuilders
                        .delete("/nodes/mynode/middlenode/myfile.txt")
                        .header("Authorization", "Bearer user2_token"))
                //.content(requestBody)
                //.contentType(MediaType.APPLICATION_JSON)
                //.accept(MediaType.APPLICATION_XML))
                //.andDo(print())
                        .andExpect(status().isOk());
        
     
    }

    
    @Test
    public void testDeleteExistingNodeInRoot() throws Exception {
    public void testLeafNodeNotFound() throws Exception {
        
        mockMvc.perform(put("/nodes/mydata1")
                .header("Authorization", "Bearer user2_token"))
                //.content(requestBody)
                //.contentType(MediaType.APPLICATION_JSON)
                //.accept(MediaType.APPLICATION_XML))
                //.andDo(print())
                .andExpect(status().isOk());
        when(nodeDao.listNode(eq("/mynode"))).thenReturn(Optional.of(getWritableDataNode("/mynode")));
        
        mockMvc.perform(MockMvcRequestBuilders
                        .delete("/nodes/mynode/notexisting")
                        .header("Authorization", "Bearer user2_token"))
                        .andExpect(status().isNotFound());
        
    }

    
    @Test
    public void testDeleteExistingNodeLeaf() throws Exception {
    public void testMiddleLevelNodeNotFound() throws Exception {
        
        mockMvc.perform(put("/nodes/mydata1")
                .header("Authorization", "Bearer user2_token"))
                //.content(requestBody)
                //.contentType(MediaType.APPLICATION_JSON)
                //.accept(MediaType.APPLICATION_XML))
                //.andDo(print())
                .andExpect(status().isOk());
        when(nodeDao.listNode(eq("/mynode"))).thenReturn(Optional.of(getWritableDataNode("/mynode")));     
        when(nodeDao.listNode(eq("/mynode/middlenode/myfile.txt"))).thenReturn(Optional.of(getWritableDataNode("/mynode/middlenode/myfile.txt")));
        
        mockMvc.perform(MockMvcRequestBuilders
                        .delete("/mynode/middlenode/myfile.txt")
                        .header("Authorization", "Bearer user2_token"))
                        .andExpect(status().isNotFound());
        
    }

    
    @Test
    public void testDeleteExistingDataLinkInRoot() throws Exception {
    public void testLinkNodeLeafDelete() throws Exception {
        
        mockMvc.perform(put("/nodes/mydata1")
        when(nodeDao.listNode(eq("/mynode"))).thenReturn(Optional.of(getWritableLinkNode("/mynode")));
        
        mockMvc.perform(MockMvcRequestBuilders
                        .delete("/nodes/mynode")
                        .header("Authorization", "Bearer user2_token"))
                //.content(requestBody)
                //.contentType(MediaType.APPLICATION_JSON)
                //.accept(MediaType.APPLICATION_XML))
                //.andDo(print())
                        .andExpect(status().isOk());
        
     
    }

    
    @Test
    public void testDeleteExistingDataLinkLeaf() throws Exception {
    public void testMiddleLevelLinkNodeDelete() throws Exception {
        
        mockMvc.perform(put("/nodes/mydata1")
                .header("Authorization", "Bearer user2_token"))
                //.content(requestBody)
                //.contentType(MediaType.APPLICATION_JSON)
                //.accept(MediaType.APPLICATION_XML))
                //.andDo(print())
                .andExpect(status().isOk());
        when(nodeDao.listNode(eq("/mynode"))).thenReturn(Optional.of(getWritableDataNode("/mynode")));        
        when(nodeDao.listNode(eq("/mynode/middlenode"))).thenReturn(Optional.of(getWritableLinkNode("/mynode/middlenode")));        
        when(nodeDao.listNode(eq("/mynode/middlenode/myfile.txt"))).thenReturn(Optional.of(getWritableDataNode("/mynode/middlenode/myfile.txt")));
        
        mockMvc.perform(MockMvcRequestBuilders
                        .delete("/nodes/mynode/middlenode/myfile.txt")
                        .header("Authorization", "Bearer user2_token"))
                        .andExpect(status().isBadRequest());
        
    }
    

    @Test
    public void testDeleteExistingNodeWithDataLinkInPath() throws Exception {
    public void testDeleteMoreLevelNodeNotAllowed() throws Exception {
        
        mockMvc.perform(put("/nodes/mydata1")
                .header("Authorization", "Bearer user2_token"))
                //.content(requestBody)
                //.contentType(MediaType.APPLICATION_JSON)
                //.accept(MediaType.APPLICATION_XML))
                //.andDo(print())
                .andExpect(status().isOk());
        when(nodeDao.listNode(eq("/mynode"))).thenReturn(Optional.of(getWritableDataNode("/mynode")));        
        when(nodeDao.listNode(eq("/mynode/middlenode"))).thenReturn(Optional.of(getWritableDataNode("/mynode/middlenode")));        
        when(nodeDao.listNode(eq("/mynode/middlenode/myfile.txt"))).thenReturn(Optional.of(getWritableDataNode("/mynode/middlenode/myfile.txt")));
        
        mockMvc.perform(MockMvcRequestBuilders
                        .delete("/nodes/mynode/middlenode/myfile.txt"))
                        .andExpect(status().isForbidden());
        
    }
*/    
    
    
    
    private Optional<Node> getRootNode() {
        ContainerNode root = new ContainerNode();
        root.setUri(URI_PREFIX + "/");
        root.getNodes().add(getDataNode());
        root.getNodes().add(getWritableDataNode());
        return Optional.of(root);
    }
    
    

    private Node getDataNode() {
    private Node getWritableDataNode() {
        DataNode node = new DataNode();
        List nodeProperties = node.getProperties();
        Property groupWriteProp = new Property();
        groupWriteProp.setUri(NodeProperties.GROUP_WRITE_URI);
        groupWriteProp.setValue("group1");
        nodeProperties.add(groupWriteProp);
        node.setUri(URI_PREFIX + "/mynode");
        return node;
    }
    
    
    private Node getWritableDataNode(String path) {
        DataNode node = new DataNode();
        List nodeProperties = node.getProperties();
        Property groupWriteProp = new Property();
        groupWriteProp.setUri(NodeProperties.GROUP_WRITE_URI);
        groupWriteProp.setValue("group1");
        nodeProperties.add(groupWriteProp);
        node.setUri(URI_PREFIX + path);
        return node;
    }
    
    

    
    
    

    private LinkNode getWritableLinkNode(String path) {
        LinkNode myNode = new LinkNode();
        // Set parent node address at /
        myNode.setUri("vos://example.com!vospace" + path);
        // Set groupwrite property
        Property groups = new Property();
        groups.setUri("ivo://ivoa.net/vospace/core#groupwrite");
        groups.setValue("group1");
        myNode.setProperties(List.of(groups));
        return myNode;
    }

}