diff --git a/src/main/java/it/inaf/oats/vospace/DeleteNodeController.java b/src/main/java/it/inaf/oats/vospace/DeleteNodeController.java index 6ca9f25a2392c39a5a1261f27a40098c987d8105..a77d23ae52b2dd35088623a6a983a11a1b520fb5 100644 --- a/src/main/java/it/inaf/oats/vospace/DeleteNodeController.java +++ b/src/main/java/it/inaf/oats/vospace/DeleteNodeController.java @@ -27,10 +27,10 @@ import org.springframework.web.bind.annotation.RestController; @RestController public class DeleteNodeController extends BaseNodeController { - private static final Logger LOG = LoggerFactory.getLogger(DeleteNodeController.class); - @Autowired - private NodeDAO nodeDAO; + DeleteNodeService deleteNodeService; + + private static final Logger LOG = LoggerFactory.getLogger(DeleteNodeController.class); @DeleteMapping(value = {"/nodes", "/nodes/**"}, produces = {MediaType.APPLICATION_XML_VALUE, MediaType.TEXT_XML_VALUE, MediaType.APPLICATION_JSON_VALUE}) @@ -38,49 +38,15 @@ public class DeleteNodeController extends BaseNodeController { String path = getPath(); LOG.debug("deleteNode called for path {}", path); - - // 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.isEmpty()) { - - // Manage root node - throw PermissionDeniedException.forPath("/"); - - } else { - - // Manage all precursors in full path - for (int i = 0; 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); - - } - - } - - if (!NodeUtils.checkIfWritable(toBeDeletedNode, principal.getName(), principal.getGroups())) { - throw PermissionDeniedException.forPath(path); - } + try { - nodeDAO.deleteNode(path); + deleteNodeService.doPreliminaryChecks(path); + deleteNodeService.deleteNode(path, principal); return ResponseEntity.ok("Node deleted"); } catch(Exception ex) { return new ResponseEntity<>(ex.getMessage(), HttpStatus.BAD_REQUEST); - } + } } diff --git a/src/main/java/it/inaf/oats/vospace/DeleteNodeService.java b/src/main/java/it/inaf/oats/vospace/DeleteNodeService.java new file mode 100644 index 0000000000000000000000000000000000000000..9366338934ea7154c68e7a2325882ddef5d77507 --- /dev/null +++ b/src/main/java/it/inaf/oats/vospace/DeleteNodeService.java @@ -0,0 +1,88 @@ +/* + * 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.oats.vospace.datamodel.NodeUtils; +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.List; +import net.ivoa.xml.vospace.v2.Node; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.EnableTransactionManagement; +import org.springframework.transaction.annotation.Isolation; +import it.inaf.ia2.aa.data.User; +import org.springframework.transaction.annotation.Transactional; + +/** + * + * @author Nicola Fulvio Calabria <nicola.calabria at inaf.it> + */ + +@Service +@EnableTransactionManagement +public class DeleteNodeService { + + @Autowired + protected NodeDAO nodeDao; + + @Value("${vospace-authority}") + protected String authority; + + @Transactional(rollbackFor = { Exception.class }, + isolation = Isolation.REPEATABLE_READ) + public void deleteNode(String path, User principal) { + + Node toBeDeletedNode = nodeDao.listNode(path) + .orElseThrow(() -> new NodeNotFoundException(path)); + + if (!NodeUtils.checkIfWritable(toBeDeletedNode, principal.getName(), principal.getGroups())) { + throw PermissionDeniedException.forPath(path); + } + + nodeDao.deleteNode(path); + + } + + public void doPreliminaryChecks(String path) throws Exception { + // 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 + 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.isEmpty()) { + + // Manage root node + throw PermissionDeniedException.forPath("/"); + + } else { + + // Manage all precursors in full path + for (int i = 0; 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); + + } + + } + } +} diff --git a/src/main/java/it/inaf/oats/vospace/ImmutableService.java b/src/main/java/it/inaf/oats/vospace/ImmutableService.java new file mode 100644 index 0000000000000000000000000000000000000000..ec7eb0ada3d30c85d0691964c2d447d11d38e852 --- /dev/null +++ b/src/main/java/it/inaf/oats/vospace/ImmutableService.java @@ -0,0 +1,40 @@ +/* + * 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.persistence.NodeDAO; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.EnableTransactionManagement; +import org.springframework.transaction.annotation.Isolation; +import org.springframework.transaction.annotation.Transactional; + +/** + * + * @author Nicola Fulvio Calabria <nicola.calabria at inaf.it> + */ +@Service +@EnableTransactionManagement +public class ImmutableService { + + @Autowired + protected NodeDAO nodeDao; + + @Value("${vospace-authority}") + protected String authority; + + @Transactional(rollbackFor = {Exception.class}, isolation = Isolation.REPEATABLE_READ) + public void setBranchImmutable(String rootNodeURI, boolean setImmutable, User user) { + + String rootNodeVosPath = URIUtils.returnVosPathFromNodeURI(rootNodeURI, authority); + + // Check if branch is busy + + } + +} diff --git a/src/main/java/it/inaf/oats/vospace/MoveService.java b/src/main/java/it/inaf/oats/vospace/MoveService.java index 1d22cf604ce0ca19f4cf8ee233d8b7362c9c01f8..96222e1c905a1f28681790f9fcadf6489426fb04 100644 --- a/src/main/java/it/inaf/oats/vospace/MoveService.java +++ b/src/main/java/it/inaf/oats/vospace/MoveService.java @@ -68,6 +68,11 @@ public class MoveService extends AbstractNodeService { throw new NodeBusyException(sourcePath); } + // TODO create immutable node exception flavor + if (nodeDao.isBranchImmutable(sourceId)) { + throw new InternalFaultException("Source branch contains immutable nodes"); + } + if (!nodeDao.isBranchWritable(sourceId, user.getName(), user.getGroups())) { throw PermissionDeniedException.forPath(sourcePath); } @@ -84,6 +89,7 @@ public class MoveService extends AbstractNodeService { if(snd.isPermissionDenied()) throw PermissionDeniedException.forPath(destinationPath); if(!snd.isWritable()) throw new InternalFaultException("Destination is not writable: "+ destinationPath); if(!snd.isContainer()) throw new InternalFaultException("Existing destination is not a container: " + destinationPath); + if(!snd.isImmutable()) throw new InternalFaultException("Destination is immutable: " + destinationPath); destinationNodeLtreePath = snd.getDestinationNodeLtreePath(); 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 c15550dfe1aac47abb0054a3efbfb4eb18443146..ab6de8f74ada5c8d3a30a6ed68a5b167038f00a7 100644 --- a/src/main/java/it/inaf/oats/vospace/persistence/NodeDAO.java +++ b/src/main/java/it/inaf/oats/vospace/persistence/NodeDAO.java @@ -257,6 +257,7 @@ public class NodeDAO { + "n.creator_id <> ?) AS is_permission_denied,\n" + "n.type = 'container' AS is_container,\n" + "n.job_id IS NOT NULL AS busy_state\n" + + "n.immutable AS is_immutable\n" + "FROM node n \n" + "LEFT JOIN location loc ON loc.location_id = n.location_id\n" + "WHERE n.node_id = id_from_vos_path(?)\n"; @@ -286,8 +287,9 @@ public class NodeDAO { Boolean isBusy = rs.getBoolean("busy_state"); Boolean isPermissionDenied = rs.getBoolean("is_permission_denied"); Boolean isSticky = rs.getBoolean("is_sticky"); + Boolean isImmutable = rs.getBoolean("is_immutable"); - ShortNodeDescriptor result = new ShortNodeDescriptor(nodePath, isContainer, isWritable, isBusy, isPermissionDenied, isSticky); + ShortNodeDescriptor result = new ShortNodeDescriptor(nodePath, isContainer, isWritable, isBusy, isPermissionDenied, isSticky, isImmutable); return Optional.of(result); }); @@ -766,14 +768,16 @@ public class NodeDAO { private final boolean busy; private final boolean permissionDenied; private final boolean sticky; + private final boolean immutable; - public ShortNodeDescriptor(String nodeLtreePath, boolean container, boolean writable, boolean busy, boolean permissionDenied, boolean sticky) { + public ShortNodeDescriptor(String nodeLtreePath, boolean container, boolean writable, boolean busy, boolean permissionDenied, boolean sticky, boolean immutable) { this.nodeLtreePath = nodeLtreePath; this.container = container; this.writable = writable; this.busy = busy; this.permissionDenied = permissionDenied; this.sticky = sticky; + this.immutable = immutable; } public String getDestinationNodeLtreePath() { @@ -784,6 +788,10 @@ public class NodeDAO { return container; } + public boolean isImmutable() { + return immutable; + } + public boolean isWritable() { return writable; }