Commit 4aaf86be authored by Sara Bertocco's avatar Sara Bertocco
Browse files

Working on redmine task #3549

parent c9721fa4
Loading
Loading
Loading
Loading
+130 −0
Original line number Diff line number Diff line
/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package it.inaf.oats.vospace;

import it.inaf.ia2.aa.data.User;
import it.inaf.oats.vospace.datamodel.NodeProperties;
import it.inaf.oats.vospace.datamodel.NodeUtils;
import it.inaf.oats.vospace.exception.ContainerNotFoundException;
import it.inaf.oats.vospace.exception.InternalFaultException;
import it.inaf.oats.vospace.exception.LinkFoundException;
import it.inaf.oats.vospace.exception.NodeNotFoundException;
import it.inaf.oats.vospace.exception.PermissionDeniedException;
import it.inaf.oats.vospace.persistence.NodeDAO;
import java.util.ArrayList;
import java.util.List;
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.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;


@RestController
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},
            produces = {MediaType.APPLICATION_XML_VALUE, MediaType.TEXT_XML_VALUE, MediaType.APPLICATION_JSON_VALUE})
    public ResponseEntity<Node> deleteNode(HttpServletRequest request, User principal) {
        
        String path = getPath();
        
        // Check if the node is present, 
        // if the node does not exist the service SHALL throw a HTTP 404 status code 
        // including a NodeNotFound fault in the entity-body 
        // If note present, got it
        Node toBeDeletedNode = nodeDAO.listNode(path)
                        .orElseThrow(() -> new NodeNotFoundException(path));               
        
        // If a parent node in the URI path is a LinkNode, the service SHALL throw 
        // a HTTP 400 status code including a LinkFound fault in the entity-body.
        // For example, given the URI path /a/b/c, the service must throw a HTTP 400 
        // status code including a LinkFound fault in the entity-body if either /a 
        // or /a/b are LinkNodes.
        List<String> pathComponents = NodeUtils.subPathComponents(path);
        if (pathComponents.size() == 0) { 
            
            // Manage root node
            throw new PermissionDeniedException("root");
            
        } else {
            
            // Manage all precursors in full path
            for (int i = 1; i < pathComponents.size(); i++) { 
                String tmpPath = pathComponents.get(i);
                Node mynode = nodeDAO.listNode(tmpPath)
                        .orElseThrow(() -> new NodeNotFoundException(tmpPath));
                if (mynode.getType().equals("vos:LinkNode") && i < pathComponents.size()-1) // a LinkNode leaf can be deleted
                    throw new LinkFoundException(tmpPath);
                            
            }
                    
        }
        
        // 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()) {
                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);
            }

            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)));
        
    }
    
}


    
    
+19 −0
Original line number Diff line number Diff line
/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package it.inaf.oats.vospace.exception;

import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;


@ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)   // Status code 500
public class InternalFaultException  extends VoSpaceException {

    public InternalFaultException(String msg) {
        super("InternalFaultException: " + msg);
    }
    
}
+207 −0
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.persistence.NodeDAO;
import java.util.Optional;
import net.ivoa.xml.vospace.v2.ContainerNode;
import net.ivoa.xml.vospace.v2.DataNode;
import net.ivoa.xml.vospace.v2.Node;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.boot.test.mock.mockito.SpyBean;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestPropertySource;
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.MediaType;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

@SpringBootTest
@ContextConfiguration(classes = {TokenFilterConfig.class})
@TestPropertySource(properties = "spring.main.allow-bean-definition-overriding=true")
@AutoConfigureMockMvc 
public class DeleteNodeControllerTest {    
    
    private static final String URI_PREFIX = "vos://example.com!vospace";
    
    @MockBean
    private NodeDAO nodeDao;

    @SpyBean
    @Autowired
    private DeleteNodeController controller;

    @Autowired
    private MockMvc mockMvc;


    @Test
    public void testDeleteRootNode() throws Exception {
        
        when(nodeDao.listNode(eq("/"))).thenReturn(getRootNode());

        mockMvc.perform(MockMvcRequestBuilders
                        .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 {
        
        when(nodeDao.listNode(eq("/mynode"))).thenReturn(Optional.of(getDataNode()));
        
        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 {
        
        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 testDeleteExistingNodeInRoot() 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 testDeleteExistingNodeLeaf() 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 testDeleteExistingDataLinkInRoot() 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 testDeleteExistingDataLinkLeaf() 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 testDeleteExistingNodeWithDataLinkInPath() 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());

     
    }
*/    
    
    

    private Optional<Node> getRootNode() {
        ContainerNode root = new ContainerNode();
        root.setUri(URI_PREFIX + "/");
        root.getNodes().add(getDataNode());
        return Optional.of(root);
    }
    
    

    private Node getDataNode() {
        DataNode node = new DataNode();
        node.setUri(URI_PREFIX + "/mynode");
        return node;
    }
    
}