Loading src/main/java/it/inaf/oats/vospace/CopyService.java +28 −7 Original line number Original line Diff line number Diff line Loading @@ -7,12 +7,12 @@ package it.inaf.oats.vospace; import it.inaf.ia2.aa.data.User; import it.inaf.ia2.aa.data.User; import it.inaf.oats.vospace.datamodel.NodeUtils; import it.inaf.oats.vospace.datamodel.NodeUtils; import it.inaf.oats.vospace.exception.InvalidArgumentException; import it.inaf.oats.vospace.exception.NodeBusyException; import it.inaf.oats.vospace.exception.NodeBusyException; import it.inaf.oats.vospace.exception.NodeNotFoundException; import it.inaf.oats.vospace.exception.PermissionDeniedException; import it.inaf.oats.vospace.persistence.NodeDAO.ShortNodeDescriptor; import it.inaf.oats.vospace.persistence.NodeDAO.ShortNodeDescriptor; import java.util.Optional; import java.util.Optional; import net.ivoa.xml.vospace.v2.Transfer; import net.ivoa.xml.vospace.v2.Transfer; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.dao.CannotSerializeTransactionException; import org.springframework.dao.CannotSerializeTransactionException; import org.springframework.stereotype.Service; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.EnableTransactionManagement; import org.springframework.transaction.annotation.EnableTransactionManagement; Loading @@ -23,9 +23,6 @@ import org.springframework.transaction.annotation.Transactional; @EnableTransactionManagement @EnableTransactionManagement public class CopyService extends AbstractNodeService { public class CopyService extends AbstractNodeService { @Autowired private NodeBranchService nodeBranchService; @Transactional(rollbackFor = {Exception.class}, isolation = Isolation.REPEATABLE_READ) @Transactional(rollbackFor = {Exception.class}, isolation = Isolation.REPEATABLE_READ) public String processCopyNodes(Transfer transfer, String jobId, User user) { public String processCopyNodes(Transfer transfer, String jobId, User user) { Loading @@ -42,13 +39,19 @@ public class CopyService extends AbstractNodeService { this.validatePath(destinationPath); this.validatePath(destinationPath); if (sourcePath.equals(destinationPath)) { if (sourcePath.equals(destinationPath)) { return null; throw new IllegalArgumentException("Cannot copy node to itself"); } // Check if destination is subpath of source // Linux-like: "cannot copy to a subdirectory of itself" if (destinationPath.startsWith(sourcePath + "/")) { throw new IllegalArgumentException("Cannot copy node to a subdirectory of its own path"); } } try { try { // check source branch for read and lock it // check source branch for read and lock it nodeBranchService.checkBranchForReadAndLock(sourcePath, jobId, user); this.checkBranchForReadAndLock(sourcePath, jobId, user); // Check destination // Check destination Optional<ShortNodeDescriptor> destShortNodeDescriptor Optional<ShortNodeDescriptor> destShortNodeDescriptor Loading Loading @@ -86,4 +89,22 @@ public class CopyService extends AbstractNodeService { } } private void checkBranchForReadAndLock(String sourcePath, String jobId, User user) { // Get source node Optional<Long> sourceIdOpt = nodeDao.getNodeId(sourcePath); long sourceId = sourceIdOpt.orElseThrow(() -> new NodeNotFoundException(sourcePath)); if (nodeDao.isBranchBusy(sourceId)) { throw new NodeBusyException(sourcePath); } if (!nodeDao.isBranchReadable(sourceId, user.getName(), user.getGroups())) { throw new PermissionDeniedException(sourcePath); } nodeDao.setBranchJobId(sourceId, jobId); } } } src/main/java/it/inaf/oats/vospace/MoveService.java +1 −1 Original line number Original line Diff line number Diff line Loading @@ -43,7 +43,7 @@ public class MoveService extends AbstractNodeService { this.validatePath(destinationPath); this.validatePath(destinationPath); if (sourcePath.equals(destinationPath)) { if (sourcePath.equals(destinationPath)) { return; throw new IllegalArgumentException("Cannot move node to itself"); } } // Check if destination is subpath of source // Check if destination is subpath of source Loading src/main/java/it/inaf/oats/vospace/NodeBranchService.javadeleted 100644 → 0 +0 −71 Original line number Original line Diff line number Diff line /* * This file is part of vospace-rest * Copyright (C) 2021 Istituto Nazionale di Astrofisica * SPDX-License-Identifier: GPL-3.0-or-later */ package it.inaf.oats.vospace; import it.inaf.ia2.aa.data.User; import it.inaf.oats.vospace.datamodel.NodeUtils; import it.inaf.oats.vospace.exception.InternalFaultException; import it.inaf.oats.vospace.exception.InvalidArgumentException; import it.inaf.oats.vospace.exception.NodeBusyException; import it.inaf.oats.vospace.exception.NodeNotFoundException; import it.inaf.oats.vospace.exception.PermissionDeniedException; import it.inaf.oats.vospace.persistence.NodeDAO; import it.inaf.oats.vospace.persistence.NodeDAO.ShortNodeDescriptor; import java.util.Optional; import javax.servlet.http.HttpServletRequest; import net.ivoa.xml.vospace.v2.Transfer; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.dao.CannotSerializeTransactionException; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.EnableTransactionManagement; import org.springframework.transaction.annotation.Isolation; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; @Service @EnableTransactionManagement public class NodeBranchService { @Autowired private NodeDAO nodeDao; @Transactional(rollbackFor = {Exception.class}, isolation = Isolation.REPEATABLE_READ, propagation = Propagation.REQUIRED) public void checkBranchForReadAndLock(String sourcePath, String jobId, User user) { try { // Get source node Optional<Long> sourceIdOpt = nodeDao.getNodeId(sourcePath); long sourceId = sourceIdOpt.orElseThrow(() -> new NodeNotFoundException(sourcePath)); if (nodeDao.isBranchBusy(sourceId)) { throw new NodeBusyException(sourcePath); } if (!nodeDao.isBranchReadable(sourceId, user.getName(), user.getGroups())) { throw new PermissionDeniedException(sourcePath); } this.lockBranch(sourceId, jobId); } catch (CannotSerializeTransactionException ex) { // Concurrent transactions attempted to modify this set of nodes throw new NodeBusyException(sourcePath); } } private void lockBranch(Long sourceRootId, String jobId) { nodeDao.setBranchJobId(sourceRootId, jobId); } public void unlockNodes(String jobId) { nodeDao.releaseBusyNodesByJobId(jobId); } } Loading
src/main/java/it/inaf/oats/vospace/CopyService.java +28 −7 Original line number Original line Diff line number Diff line Loading @@ -7,12 +7,12 @@ package it.inaf.oats.vospace; import it.inaf.ia2.aa.data.User; import it.inaf.ia2.aa.data.User; import it.inaf.oats.vospace.datamodel.NodeUtils; import it.inaf.oats.vospace.datamodel.NodeUtils; import it.inaf.oats.vospace.exception.InvalidArgumentException; import it.inaf.oats.vospace.exception.NodeBusyException; import it.inaf.oats.vospace.exception.NodeBusyException; import it.inaf.oats.vospace.exception.NodeNotFoundException; import it.inaf.oats.vospace.exception.PermissionDeniedException; import it.inaf.oats.vospace.persistence.NodeDAO.ShortNodeDescriptor; import it.inaf.oats.vospace.persistence.NodeDAO.ShortNodeDescriptor; import java.util.Optional; import java.util.Optional; import net.ivoa.xml.vospace.v2.Transfer; import net.ivoa.xml.vospace.v2.Transfer; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.dao.CannotSerializeTransactionException; import org.springframework.dao.CannotSerializeTransactionException; import org.springframework.stereotype.Service; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.EnableTransactionManagement; import org.springframework.transaction.annotation.EnableTransactionManagement; Loading @@ -23,9 +23,6 @@ import org.springframework.transaction.annotation.Transactional; @EnableTransactionManagement @EnableTransactionManagement public class CopyService extends AbstractNodeService { public class CopyService extends AbstractNodeService { @Autowired private NodeBranchService nodeBranchService; @Transactional(rollbackFor = {Exception.class}, isolation = Isolation.REPEATABLE_READ) @Transactional(rollbackFor = {Exception.class}, isolation = Isolation.REPEATABLE_READ) public String processCopyNodes(Transfer transfer, String jobId, User user) { public String processCopyNodes(Transfer transfer, String jobId, User user) { Loading @@ -42,13 +39,19 @@ public class CopyService extends AbstractNodeService { this.validatePath(destinationPath); this.validatePath(destinationPath); if (sourcePath.equals(destinationPath)) { if (sourcePath.equals(destinationPath)) { return null; throw new IllegalArgumentException("Cannot copy node to itself"); } // Check if destination is subpath of source // Linux-like: "cannot copy to a subdirectory of itself" if (destinationPath.startsWith(sourcePath + "/")) { throw new IllegalArgumentException("Cannot copy node to a subdirectory of its own path"); } } try { try { // check source branch for read and lock it // check source branch for read and lock it nodeBranchService.checkBranchForReadAndLock(sourcePath, jobId, user); this.checkBranchForReadAndLock(sourcePath, jobId, user); // Check destination // Check destination Optional<ShortNodeDescriptor> destShortNodeDescriptor Optional<ShortNodeDescriptor> destShortNodeDescriptor Loading Loading @@ -86,4 +89,22 @@ public class CopyService extends AbstractNodeService { } } private void checkBranchForReadAndLock(String sourcePath, String jobId, User user) { // Get source node Optional<Long> sourceIdOpt = nodeDao.getNodeId(sourcePath); long sourceId = sourceIdOpt.orElseThrow(() -> new NodeNotFoundException(sourcePath)); if (nodeDao.isBranchBusy(sourceId)) { throw new NodeBusyException(sourcePath); } if (!nodeDao.isBranchReadable(sourceId, user.getName(), user.getGroups())) { throw new PermissionDeniedException(sourcePath); } nodeDao.setBranchJobId(sourceId, jobId); } } }
src/main/java/it/inaf/oats/vospace/MoveService.java +1 −1 Original line number Original line Diff line number Diff line Loading @@ -43,7 +43,7 @@ public class MoveService extends AbstractNodeService { this.validatePath(destinationPath); this.validatePath(destinationPath); if (sourcePath.equals(destinationPath)) { if (sourcePath.equals(destinationPath)) { return; throw new IllegalArgumentException("Cannot move node to itself"); } } // Check if destination is subpath of source // Check if destination is subpath of source Loading
src/main/java/it/inaf/oats/vospace/NodeBranchService.javadeleted 100644 → 0 +0 −71 Original line number Original line Diff line number Diff line /* * This file is part of vospace-rest * Copyright (C) 2021 Istituto Nazionale di Astrofisica * SPDX-License-Identifier: GPL-3.0-or-later */ package it.inaf.oats.vospace; import it.inaf.ia2.aa.data.User; import it.inaf.oats.vospace.datamodel.NodeUtils; import it.inaf.oats.vospace.exception.InternalFaultException; import it.inaf.oats.vospace.exception.InvalidArgumentException; import it.inaf.oats.vospace.exception.NodeBusyException; import it.inaf.oats.vospace.exception.NodeNotFoundException; import it.inaf.oats.vospace.exception.PermissionDeniedException; import it.inaf.oats.vospace.persistence.NodeDAO; import it.inaf.oats.vospace.persistence.NodeDAO.ShortNodeDescriptor; import java.util.Optional; import javax.servlet.http.HttpServletRequest; import net.ivoa.xml.vospace.v2.Transfer; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.dao.CannotSerializeTransactionException; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.EnableTransactionManagement; import org.springframework.transaction.annotation.Isolation; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; @Service @EnableTransactionManagement public class NodeBranchService { @Autowired private NodeDAO nodeDao; @Transactional(rollbackFor = {Exception.class}, isolation = Isolation.REPEATABLE_READ, propagation = Propagation.REQUIRED) public void checkBranchForReadAndLock(String sourcePath, String jobId, User user) { try { // Get source node Optional<Long> sourceIdOpt = nodeDao.getNodeId(sourcePath); long sourceId = sourceIdOpt.orElseThrow(() -> new NodeNotFoundException(sourcePath)); if (nodeDao.isBranchBusy(sourceId)) { throw new NodeBusyException(sourcePath); } if (!nodeDao.isBranchReadable(sourceId, user.getName(), user.getGroups())) { throw new PermissionDeniedException(sourcePath); } this.lockBranch(sourceId, jobId); } catch (CannotSerializeTransactionException ex) { // Concurrent transactions attempted to modify this set of nodes throw new NodeBusyException(sourcePath); } } private void lockBranch(Long sourceRootId, String jobId) { nodeDao.setBranchJobId(sourceRootId, jobId); } public void unlockNodes(String jobId) { nodeDao.releaseBusyNodesByJobId(jobId); } }