diff --git a/src/main/java/it/inaf/oats/vospace/DeleteNodeController.java b/src/main/java/it/inaf/oats/vospace/DeleteNodeController.java
new file mode 100644
index 0000000000000000000000000000000000000000..54b5661eeac789dd67db965a1dfa127041066f51
--- /dev/null
+++ b/src/main/java/it/inaf/oats/vospace/DeleteNodeController.java
@@ -0,0 +1,130 @@
+/*
+ * 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)));
+        
+    }
+    
+}
+
+
+    
+    
+
diff --git a/src/main/java/it/inaf/oats/vospace/exception/InternalFaultException.java b/src/main/java/it/inaf/oats/vospace/exception/InternalFaultException.java
new file mode 100644
index 0000000000000000000000000000000000000000..2429c070a061a09f10ea8eb712c1993847dcd1f8
--- /dev/null
+++ b/src/main/java/it/inaf/oats/vospace/exception/InternalFaultException.java
@@ -0,0 +1,19 @@
+/*
+ * 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);
+    }
+    
+}
diff --git a/src/test/java/it/inaf/oats/vospace/DeleteNodeControllerTest.java b/src/test/java/it/inaf/oats/vospace/DeleteNodeControllerTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..e9073202da5f59c7ee019ec3dd1d943b2fc2d470
--- /dev/null
+++ b/src/test/java/it/inaf/oats/vospace/DeleteNodeControllerTest.java
@@ -0,0 +1,207 @@
+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;
+    }
+    
+}