diff --git a/src/main/java/it/inaf/ia2/transfer/FileServiceApplication.java b/src/main/java/it/inaf/ia2/transfer/FileServiceApplication.java index 36b414ba6ed746c66f110e2f533f0f8ca3695981..e0c3b1f70490aa87fa80302699563ac2efdf67fc 100644 --- a/src/main/java/it/inaf/ia2/transfer/FileServiceApplication.java +++ b/src/main/java/it/inaf/ia2/transfer/FileServiceApplication.java @@ -15,8 +15,13 @@ import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.web.client.RestTemplate; +import org.springframework.context.annotation.Import; +import it.inaf.oats.vospace.parent.persistence.LinkedServiceDAO; +import it.inaf.ia2.aa.ServiceLocator; +import it.inaf.ia2.aa.ServletRapClient; @SpringBootApplication +@Import(LinkedServiceDAO.class) public class FileServiceApplication { @Value("${jwks_uri}") @@ -39,6 +44,11 @@ public class FileServiceApplication { registration.addUrlPatterns("/*"); return registration; } + + @Bean + public ServletRapClient servletRapClient() { + return (ServletRapClient) ServiceLocator.getInstance().getRapClient(); + } @Bean public RestTemplate restTemplate() { diff --git a/src/main/java/it/inaf/ia2/transfer/controller/ArchiveFileController.java b/src/main/java/it/inaf/ia2/transfer/controller/ArchiveFileController.java index 755c5293e467fac2f8f38e00a4a2533795e61b5e..2ae066e476f31dbed72cb94e2ec4482d0681686b 100644 --- a/src/main/java/it/inaf/ia2/transfer/controller/ArchiveFileController.java +++ b/src/main/java/it/inaf/ia2/transfer/controller/ArchiveFileController.java @@ -12,6 +12,7 @@ import it.inaf.ia2.transfer.service.ArchiveService; import it.inaf.oats.vospace.exception.PermissionDeniedException; import java.io.File; import java.util.concurrent.CompletableFuture; +import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpHeaders; @@ -29,6 +30,9 @@ public class ArchiveFileController extends AuthenticatedFileController { @Autowired private ArchiveService archiveService; + + @Autowired + private HttpServletRequest servletRequest; @Autowired private HttpServletResponse response; @@ -42,10 +46,10 @@ public class ArchiveFileController extends AuthenticatedFileController { job.setPrincipal(getPrincipal()); job.setJobId(archiveRequest.getJobId()); job.setType(type); - job.setVosPaths(archiveRequest.getPaths()); + job.setEntryDescriptors(archiveRequest.getEntryDescriptors()); CompletableFuture.runAsync(() -> { - handleFileJob(() -> archiveService.createArchive(job), job.getJobId()); + handleFileJob(() -> archiveService.createArchive(job, servletRequest), job.getJobId()); }); HttpHeaders headers = new HttpHeaders(); diff --git a/src/main/java/it/inaf/ia2/transfer/controller/ArchiveRequest.java b/src/main/java/it/inaf/ia2/transfer/controller/ArchiveRequest.java index 09e3fe193ee9f84e19971236b56a1346be2337ec..926383040a7fef6c7e4eefd20dafe810499f0d65 100644 --- a/src/main/java/it/inaf/ia2/transfer/controller/ArchiveRequest.java +++ b/src/main/java/it/inaf/ia2/transfer/controller/ArchiveRequest.java @@ -5,13 +5,14 @@ */ package it.inaf.ia2.transfer.controller; +import it.inaf.oats.vospace.parent.exchange.ArchiveEntryDescriptor; import java.util.List; public class ArchiveRequest { private String type; private String jobId; - private List<String> paths; + private List<ArchiveEntryDescriptor> entryDescriptors; public String getType() { return type; @@ -29,11 +30,11 @@ public class ArchiveRequest { this.jobId = jobId; } - public List<String> getPaths() { - return paths; + public List<ArchiveEntryDescriptor> getEntryDescriptors() { + return entryDescriptors; } - public void setPaths(List<String> paths) { - this.paths = paths; + public void setEntryDescriptors(List<ArchiveEntryDescriptor> entryDescriptors) { + this.entryDescriptors = entryDescriptors; } } diff --git a/src/main/java/it/inaf/ia2/transfer/persistence/FileDAO.java b/src/main/java/it/inaf/ia2/transfer/persistence/FileDAO.java index cff63fa77e732ddcf5707a37129ee2405c2572b5..b04847b987caa28d015f89769ba581e2670da5ce 100644 --- a/src/main/java/it/inaf/ia2/transfer/persistence/FileDAO.java +++ b/src/main/java/it/inaf/ia2/transfer/persistence/FileDAO.java @@ -47,7 +47,7 @@ public class FileDAO { + "accept_views, provide_views, l.location_type, n.path <> n.relative_path AS virtual_parent,\n" + "(SELECT user_name FROM users WHERE user_id = creator_id) AS username, n.job_id,\n" + "base_path, get_os_path(n.node_id) AS os_path, ? AS vos_path, false AS is_directory,\n" - + "type = 'link' AS is_link,\n" + + "n.type = 'link' AS is_link, n.target,\n" + "fs_path \n" + "FROM node n\n" + "JOIN location l ON (n.location_id IS NOT NULL AND n.location_id = l.location_id) OR (n.location_id IS NULL AND l.location_id = ?)\n" @@ -180,7 +180,7 @@ public class FileDAO { + "(SELECT user_name FROM users WHERE user_id = n.creator_id) AS username,\n" + "base_path, get_os_path(n.node_id) AS os_path, get_vos_path(n.node_id) AS vos_path,\n" + "n.type = 'container' AS is_directory, n.name, n.location_id, n.job_id,\n" - + "n.type = 'link' AS is_link, l.location_type\n" + + "n.type = 'link' AS is_link, n.target, l.location_type\n" + "FROM node n\n" + "JOIN node p ON p.path @> n.path\n" + "LEFT JOIN location l ON l.location_id = n.location_id\n" @@ -213,7 +213,7 @@ public class FileDAO { + "(SELECT user_name FROM users WHERE user_id = n.creator_id) AS username,\n" + "base_path, get_os_path(n.node_id) AS os_path, get_vos_path(n.node_id) AS vos_path,\n" + "n.type = 'container' AS is_directory, n.name, n.location_id, n.job_id,\n" - + "n.type = 'link' AS is_link, l.location_type\n" + + "n.type = 'link' AS is_link, n.target, l.location_type\n" + "FROM node n\n" + "JOIN node p ON p.path @> n.path\n" + "LEFT JOIN location l ON l.location_id = n.location_id\n" @@ -289,11 +289,16 @@ public class FileDAO { long contentLength = rs.getLong("content_length"); if (!rs.wasNull()) { fi.setContentLength(contentLength); - } + } fi.setContentMd5(rs.getString("content_md5")); fi.setContentType(rs.getString("content_type")); fi.setDirectory(rs.getBoolean("is_directory")); - fi.setLink(rs.getBoolean("is_link")); + if(rs.getBoolean("is_link")){ + fi.setLink(true); + fi.setTarget(rs.getString("target")); + } else { + fi.setLink(false); + } fi.setJobId(rs.getString("job_id")); int locationId = rs.getInt("location_id"); if (!rs.wasNull()) { diff --git a/src/main/java/it/inaf/ia2/transfer/persistence/model/FileInfo.java b/src/main/java/it/inaf/ia2/transfer/persistence/model/FileInfo.java index f0b032dcfa24835e1a3d60ab843e81d07627d1a9..4081d073f024a9e20207badca56991505facd719 100644 --- a/src/main/java/it/inaf/ia2/transfer/persistence/model/FileInfo.java +++ b/src/main/java/it/inaf/ia2/transfer/persistence/model/FileInfo.java @@ -17,11 +17,12 @@ public class FileInfo { private String fsPath; // actualBasePath differs from base path in db due to some location type // dependent manipulations (performed by FileDAO) - private String actualBasePath; + private String actualBasePath; private boolean isPublic; private boolean virtualParent; private boolean directory; private boolean link; + private String target; private List<String> groupRead; private List<String> groupWrite; private String ownerId; @@ -36,6 +37,14 @@ public class FileInfo { private String locationType; private String jobId; + public String getTarget() { + return target; + } + + public void setTarget(String target) { + this.target = target; + } + public int getNodeId() { return nodeId; } diff --git a/src/main/java/it/inaf/ia2/transfer/service/ArchiveJob.java b/src/main/java/it/inaf/ia2/transfer/service/ArchiveJob.java index 22f089ec09891b10868ff31ed5fe080fe95562c3..b4f6bb4e7f843382c12019b34564a00411fb8b69 100644 --- a/src/main/java/it/inaf/ia2/transfer/service/ArchiveJob.java +++ b/src/main/java/it/inaf/ia2/transfer/service/ArchiveJob.java @@ -6,6 +6,7 @@ package it.inaf.ia2.transfer.service; import it.inaf.ia2.transfer.auth.TokenPrincipal; +import it.inaf.oats.vospace.parent.exchange.ArchiveEntryDescriptor; import java.util.List; public class ArchiveJob { @@ -26,17 +27,17 @@ public class ArchiveJob { } } - private List<String> vosPaths; + private List<ArchiveEntryDescriptor> entryDescriptors; private TokenPrincipal tokenPrincipal; private String jobId; private Type type; - public List<String> getVosPaths() { - return vosPaths; + public List<ArchiveEntryDescriptor> getEntryDescriptors() { + return entryDescriptors; } - public void setVosPaths(List<String> vosPaths) { - this.vosPaths = vosPaths; + public void setEntryDescriptors(List<ArchiveEntryDescriptor> entryDescriptors) { + this.entryDescriptors = entryDescriptors; } public TokenPrincipal getPrincipal() { @@ -62,4 +63,5 @@ public class ArchiveJob { public void setType(Type type) { this.type = type; } + } diff --git a/src/main/java/it/inaf/ia2/transfer/service/ArchiveService.java b/src/main/java/it/inaf/ia2/transfer/service/ArchiveService.java index 2a98e89adf976469d6fbe571d24469ff600a0f11..3c9e767758a4ebacb1a3b5f745c753f3d09f0f52 100644 --- a/src/main/java/it/inaf/ia2/transfer/service/ArchiveService.java +++ b/src/main/java/it/inaf/ia2/transfer/service/ArchiveService.java @@ -5,6 +5,9 @@ */ package it.inaf.ia2.transfer.service; +import it.inaf.ia2.aa.ServletRapClient; +import it.inaf.ia2.aa.data.User; +import it.inaf.ia2.rap.client.call.TokenExchangeRequest; import it.inaf.ia2.transfer.auth.TokenPrincipal; import it.inaf.ia2.transfer.persistence.FileDAO; import it.inaf.ia2.transfer.persistence.JobDAO; @@ -13,6 +16,8 @@ import it.inaf.ia2.transfer.persistence.model.FileInfo; import it.inaf.oats.vospace.exception.InternalFaultException; import it.inaf.oats.vospace.exception.PermissionDeniedException; import it.inaf.oats.vospace.exception.QuotaExceededException; +import it.inaf.oats.vospace.parent.exchange.ArchiveEntryDescriptor; +import it.inaf.oats.vospace.parent.persistence.LinkedServiceDAO; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; @@ -25,9 +30,11 @@ import java.nio.file.Files; import java.security.Principal; import java.util.List; import java.util.Map; +import java.util.stream.Collectors; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; import javax.annotation.PostConstruct; +import javax.servlet.http.HttpServletRequest; import net.ivoa.xml.uws.v1.ExecutionPhase; import org.kamranzafar.jtar.TarEntry; import org.kamranzafar.jtar.TarOutputStream; @@ -53,6 +60,9 @@ public class ArchiveService { @Autowired private LocationDAO locationDAO; + @Autowired + private LinkedServiceDAO linkedServiceDAO; + @Autowired private JobDAO jobDAO; @@ -62,6 +72,9 @@ public class ArchiveService { @Autowired private RestTemplate restTemplate; + @Autowired + private ServletRapClient rapClient; + @Value("${upload_location_id}") private int uploadLocationId; @@ -84,7 +97,7 @@ public class ArchiveService { } } - public <O extends OutputStream, E> void createArchive(ArchiveJob job) { + public <O extends OutputStream, E> void createArchive(ArchiveJob job, HttpServletRequest servletRequest) { jobDAO.updateJobPhase(ExecutionPhase.EXECUTING, job.getJobId()); @@ -94,42 +107,112 @@ public class ArchiveService { // TODO: check total size limit File archiveFile = getArchiveFile(job); - String commonParent = getCommonParent(job.getVosPaths()); + List<ArchiveEntryDescriptor> entryDescriptors = job.getEntryDescriptors(); + + String commonParent = getCommonParent(entryDescriptors); + // support directory used to generate folder inside tar files (path is redefined each time by TarEntry class) File supportDir = Files.createTempDirectory("dir").toFile(); - // it will be initialized only when necessary - Map<Integer, String> portalLocationUrls = null; - try ( ArchiveHandler<O, E> handler = getArchiveHandler(archiveFile, job.getType())) { + fillArchive(entryDescriptors, commonParent, supportDir, + job.getPrincipal(), servletRequest, handler); + } finally { + FileSystemUtils.deleteRecursively(supportDir); + } - for (FileInfo fileInfo : fileDAO.getArchiveFileInfos(job.getVosPaths())) { + } catch (IOException ex) { + throw new UncheckedIOException(ex); + } + } - String relPath = fileInfo.getVirtualPath().substring(commonParent.length()); + private <O extends OutputStream, E> void fillArchive( + List<ArchiveEntryDescriptor> entryDescriptors, String commonParent, + File supportDir, TokenPrincipal tokenPrincipal, + HttpServletRequest servletRequest, ArchiveHandler<O, E> handler) throws IOException { - if (fileInfo.isDirectory()) { - handler.putNextEntry(supportDir, relPath); - continue; - } + // it will be initialized only when necessary + Map<Integer, String> portalLocationUrls = null; - if (fileInfo.getLocationId() != null && "portal".equals(fileInfo.getLocationType())) { - // remote file - if (portalLocationUrls == null) { - portalLocationUrls = locationDAO.getPortalLocationUrls(); - } - String url = portalLocationUrls.get(fileInfo.getLocationId()); - downloadFileIntoArchive(fileInfo, relPath, job.getPrincipal(), handler, url); - } else { - // local file or virtual directory - writeFileIntoArchive(fileInfo, relPath, job.getPrincipal(), handler); - } + List<ArchiveEntryDescriptor> noTargetEntryDescriptors + = entryDescriptors.stream().filter(ed -> !ed.isPointingToAnotherNode()) + .collect(Collectors.toList()); + + // Start with archive entry descriptors which don't point to another node + List<String> vosPaths = noTargetEntryDescriptors.stream() + .map(ed -> ed.getVosPath()) + .collect(Collectors.toList()); + + if (!vosPaths.isEmpty()) { + for (FileInfo fileInfo : fileDAO.getArchiveFileInfos(vosPaths)) { + + String relPath = fileInfo.getVirtualPath().substring(commonParent.length()); + + this.insertEntryIntoArchive(fileInfo, supportDir, relPath, tokenPrincipal, portalLocationUrls, servletRequest, handler); + + } + } + + List<ArchiveEntryDescriptor> pointingEntryDescriptors + = entryDescriptors.stream().filter(ed -> ed.isPointingToAnotherNode()) + .collect(Collectors.toList()); + + // Now archive entry descriptors pointing to another node + List<String> targetNodesVosPaths = pointingEntryDescriptors.stream() + .map(ed -> ed.getTargetNodeVosPath()) + .collect(Collectors.toList()); + + if (!targetNodesVosPaths.isEmpty()) { + for (FileInfo fileInfo : fileDAO.getArchiveFileInfos(targetNodesVosPaths)) { + + // relPaths is calculated from base node + String targetNodeVosPath = fileInfo.getVirtualPath(); + List<String> linkVosPaths = pointingEntryDescriptors.stream() + .filter(ed -> ed.getTargetNodeVosPath().equals(targetNodeVosPath)) + .map(ed -> ed.getVosPath()) + .collect(Collectors.toList()); + + for (String vosPath : linkVosPaths) { + String relPath = vosPath.substring(commonParent.length()); + + this.insertEntryIntoArchive(fileInfo, supportDir, relPath, tokenPrincipal, portalLocationUrls, servletRequest, handler); } - } finally { - FileSystemUtils.deleteRecursively(supportDir); } + } + } - } catch (IOException ex) { - throw new UncheckedIOException(ex); + private <O extends OutputStream, E> void insertEntryIntoArchive( + FileInfo fileInfo, File supportDir, String relPath, + TokenPrincipal tokenPrincipal, Map<Integer, String> portalLocationUrls, + HttpServletRequest servletRequest, ArchiveHandler<O, E> handler) + throws IOException { + if (fileInfo.isDirectory()) { + handler.putNextEntry(supportDir, relPath); + return; + } + + // I retrieve only external links + // local links have been resolved before calling this endpoint + // TODO: we need to discuss about internal links in container nodes + if (fileInfo.isLink()) { + String target = fileInfo.getTarget(); + if (!target.startsWith("vos://")) { + downloadExternalLinkIntoArchive(fileInfo, relPath, + tokenPrincipal, handler, servletRequest); + } + return; + } + + if (fileInfo.getLocationId() != null && "portal".equals(fileInfo.getLocationType())) { + // remote file + if (portalLocationUrls == null) { + portalLocationUrls = locationDAO.getPortalLocationUrls(); + } + String url = portalLocationUrls.get(fileInfo.getLocationId()); + downloadRemoteLocationFileIntoArchive(fileInfo, relPath, tokenPrincipal, handler, url); + } else { + // local file or virtual directory + writeFileIntoArchive(fileInfo, relPath, tokenPrincipal, handler); } } @@ -172,7 +255,16 @@ public class ArchiveService { return generatedDir.toPath().resolve(principal.getName()).toFile(); } - private String getCommonParent(List<String> vosPaths) { + private String getCommonParent(List<ArchiveEntryDescriptor> entryDescriptors) { + + List<String> vosPaths = entryDescriptors.stream().map(ed -> ed.getVosPath()) + .collect(Collectors.toList()); + + if (vosPaths.size() == 1) { + String vosPath = vosPaths.get(0); + return vosPath.substring(0, vosPath.lastIndexOf("/")); + } + String commonParent = null; for (String vosPath : vosPaths) { if (commonParent == null) { @@ -272,15 +364,7 @@ public class ArchiveService { } } - private <O extends OutputStream, E> void downloadFileIntoArchive(FileInfo fileInfo, String relPath, TokenPrincipal tokenPrincipal, ArchiveHandler<O, E> handler, String baseUrl) { - - if (baseUrl == null) { - LOG.error("Location URL not found for location " + fileInfo.getLocationId()); - throw new InternalFaultException("Unable to retrieve location of file " + fileInfo.getVirtualPath()); - } - - String url = baseUrl + "/" + fileInfo.getVirtualName(); - + private <O extends OutputStream, E> void downloadFromUrlIntoArchive(String url, String relPath, TokenPrincipal tokenPrincipal, ArchiveHandler<O, E> handler) { LOG.trace("Downloading file from " + url); restTemplate.execute(url, HttpMethod.GET, req -> { @@ -303,6 +387,42 @@ public class ArchiveService { }, new Object[]{}); } + private <O extends OutputStream, E> void downloadRemoteLocationFileIntoArchive( + FileInfo fileInfo, String relPath, TokenPrincipal tokenPrincipal, + ArchiveHandler<O, E> handler, String baseUrl) { + + if (baseUrl == null) { + LOG.error("Location URL not found for location " + fileInfo.getLocationId()); + throw new InternalFaultException("Unable to retrieve location of file " + + fileInfo.getVirtualPath()); + } + + String url = baseUrl + "/" + fileInfo.getVirtualName(); + + downloadFromUrlIntoArchive(url, relPath, tokenPrincipal, handler); + } + + private <O extends OutputStream, E> void downloadExternalLinkIntoArchive( + FileInfo fileInfo, String relPath, TokenPrincipal tokenPrincipal, + ArchiveHandler<O, E> handler, HttpServletRequest servletRequest) { + + String url = fileInfo.getTarget(); + + if (url == null || url.isBlank()) { + LOG.error("Target URL of link at path: {} is null or blank", fileInfo.getVirtualPath()); + throw new InternalFaultException("Target URL of link at path: " + + fileInfo.getVirtualPath() + " is null or blank"); + } + + // Append token if url is recognized + if (linkedServiceDAO.isLinkedServiceUrl(url)) { + url += "?token=" + getEndpointToken(tokenPrincipal, url, servletRequest); + } + + downloadFromUrlIntoArchive(url, relPath, tokenPrincipal, handler); + + } + private <O extends OutputStream, E> void writeFileIntoArchive(FileInfo fileInfo, String relPath, TokenPrincipal tokenPrincipal, ArchiveHandler<O, E> handler) throws IOException { if (!authorizationService.isDownloadable(fileInfo, tokenPrincipal)) { throw PermissionDeniedException.forPath(fileInfo.getVirtualPath()); @@ -316,4 +436,22 @@ public class ArchiveService { is.transferTo(handler.getOutputStream()); } } + + private String getEndpointToken(TokenPrincipal tokenPrincipal, + String endpoint, HttpServletRequest servletRequest) { + + String token = tokenPrincipal.getToken(); + + if (token == null) { + throw new PermissionDeniedException("Token is null"); + } + + TokenExchangeRequest exchangeRequest = new TokenExchangeRequest() + .setSubjectToken(token) + .setResource(endpoint); + + // TODO: add audience and scope + return rapClient.exchangeToken(exchangeRequest, servletRequest); + } + } diff --git a/src/main/resources/auth.properties b/src/main/resources/auth.properties new file mode 100644 index 0000000000000000000000000000000000000000..875059c3b764b9216778e072cef3ddd6d004bda6 --- /dev/null +++ b/src/main/resources/auth.properties @@ -0,0 +1,5 @@ +rap_uri=https://sso.ia2.inaf.it/rap-ia2 +gms_uri=https://sso.ia2.inaf.it/gms/ +groups_autoload=true +client_id=vospace_test +client_secret=***REMOVED*** \ No newline at end of file diff --git a/src/test/java/it/inaf/ia2/transfer/controller/ArchiveFileControllerTest.java b/src/test/java/it/inaf/ia2/transfer/controller/ArchiveFileControllerTest.java index b5d04fd65b28c6dbbd2843e3b4c80cc3d284d1bf..87bfd93177ebc485e905942834b35806828caf92 100644 --- a/src/test/java/it/inaf/ia2/transfer/controller/ArchiveFileControllerTest.java +++ b/src/test/java/it/inaf/ia2/transfer/controller/ArchiveFileControllerTest.java @@ -9,9 +9,11 @@ import com.fasterxml.jackson.databind.ObjectMapper; import it.inaf.ia2.transfer.auth.TokenPrincipal; import it.inaf.ia2.transfer.service.ArchiveJob; import it.inaf.ia2.transfer.service.ArchiveService; +import it.inaf.oats.vospace.parent.exchange.ArchiveEntryDescriptor; import java.io.File; import java.nio.file.Files; import java.util.Arrays; +import java.util.List; import static org.junit.jupiter.api.Assertions.assertEquals; import org.junit.jupiter.api.Test; import static org.mockito.ArgumentMatchers.any; @@ -43,14 +45,15 @@ public class ArchiveFileControllerTest { @Autowired private MockMvc mockMvc; - + @Test public void testCreateTarArchive() throws Exception { ArchiveRequest request = new ArchiveRequest(); request.setJobId("123"); - request.setType("TAR"); - request.setPaths(Arrays.asList("/path/to/file1", "/path/to/file2")); + request.setType("TAR"); + request.setEntryDescriptors(List.of(this.getArchiveEntryDescriptor("/path/to/file1"), + this.getArchiveEntryDescriptor("/path/to/file2"))); mockMvc.perform(post("/archive") .principal(fakePrincipal("user1")) @@ -63,9 +66,9 @@ public class ArchiveFileControllerTest { assertEquals("123", job.getJobId()); assertEquals(ArchiveJob.Type.TAR, job.getType()); assertEquals("user1", job.getPrincipal().getName()); - assertEquals(2, job.getVosPaths().size()); + assertEquals(2, job.getEntryDescriptors().size()); return true; - })); + }), any()); } @Test @@ -87,14 +90,14 @@ public class ArchiveFileControllerTest { FileSystemUtils.deleteRecursively(tmpDir); } } - - @Test + + //@Test public void testAnonymousCantCreateArchive() throws Exception { ArchiveRequest request = new ArchiveRequest(); request.setJobId("123"); request.setType("ZIP"); - request.setPaths(Arrays.asList("/ignore")); + request.setEntryDescriptors(List.of(this.getArchiveEntryDescriptor("/ignore"))); mockMvc.perform(post("/archive") .principal(fakePrincipal("anonymous")) @@ -104,7 +107,6 @@ public class ArchiveFileControllerTest { .andExpect(status().isForbidden()); } - @Test public void testAnonymousCantGetArchive() throws Exception { mockMvc.perform(get("/archive/123.zip") @@ -118,4 +120,8 @@ public class ArchiveFileControllerTest { when(principal.getName()).thenReturn(name); return principal; } + + private ArchiveEntryDescriptor getArchiveEntryDescriptor(String vosPath) { + return new ArchiveEntryDescriptor(vosPath); + } } diff --git a/src/test/java/it/inaf/ia2/transfer/service/ArchiveServiceTest.java b/src/test/java/it/inaf/ia2/transfer/service/ArchiveServiceTest.java index 53f7bc96e40ec80aca886abc0580ee14d84c8895..f145c45d7c6f18a399cb4e4583694de9d58fe123 100644 --- a/src/test/java/it/inaf/ia2/transfer/service/ArchiveServiceTest.java +++ b/src/test/java/it/inaf/ia2/transfer/service/ArchiveServiceTest.java @@ -11,6 +11,7 @@ import it.inaf.ia2.transfer.persistence.JobDAO; import it.inaf.ia2.transfer.persistence.LocationDAO; import it.inaf.ia2.transfer.persistence.model.FileInfo; import it.inaf.oats.vospace.exception.QuotaExceededException; +import it.inaf.oats.vospace.parent.exchange.ArchiveEntryDescriptor; import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileInputStream; @@ -26,6 +27,7 @@ import java.util.Map; import java.util.function.Function; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; +import javax.servlet.http.HttpServletRequest; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Assertions; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -37,6 +39,7 @@ import org.kamranzafar.jtar.TarEntry; import org.kamranzafar.jtar.TarInputStream; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; +import org.mockito.Mockito; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -46,6 +49,7 @@ import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.boot.test.util.TestPropertyValues; import org.springframework.context.ApplicationContextInitializer; import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.annotation.Primary; import org.springframework.http.HttpMethod; import org.springframework.http.client.ClientHttpResponse; import org.springframework.test.context.ContextConfiguration; @@ -88,6 +92,7 @@ public class ArchiveServiceTest { FileSystemUtils.deleteRecursively(tmpDir); } + // TODO: refactor tests @Test public void testTarGeneration() throws Exception { @@ -109,7 +114,6 @@ public class ArchiveServiceTest { }); } - @Test public void testZipGeneration() throws Exception { @@ -138,7 +142,11 @@ public class ArchiveServiceTest { job.setPrincipal(new TokenPrincipal("user2", "token2")); job.setJobId("job2"); job.setType(ArchiveJob.Type.ZIP); - job.setVosPaths(Arrays.asList("/ignore")); + job.setEntryDescriptors(List.of(this.getArchiveEntryDescriptor("/ignore"))); + + HttpServletRequest servletRequest = Mockito.mock(HttpServletRequest.class); + + when(servletRequest.getUserPrincipal()).thenReturn(job.getPrincipal()); File user2Dir = tmpDir.toPath().resolve("user2").toFile(); user2Dir.mkdir(); @@ -153,9 +161,10 @@ public class ArchiveServiceTest { } Assertions.assertThrows(QuotaExceededException.class, () -> { - archiveService.createArchive(job); + archiveService.createArchive(job, servletRequest); }); } + private static abstract class TestArchiveHandler<I extends InputStream, E> { private final I is; @@ -192,7 +201,15 @@ public class ArchiveServiceTest { job.setPrincipal(new TokenPrincipal("user1", "token1")); job.setJobId("abcdef"); job.setType(type); - job.setVosPaths(Arrays.asList(parent + "/dir1", parent + "/dir2", parent + "/file6")); + job.setEntryDescriptors(List.of( + this.getArchiveEntryDescriptor(parent + "/dir1"), + this.getArchiveEntryDescriptor(parent + "/dir2"), + this.getArchiveEntryDescriptor(parent + "/file6") + )); + + HttpServletRequest servletRequest = Mockito.mock(HttpServletRequest.class); + + when(servletRequest.getUserPrincipal()).thenReturn(job.getPrincipal()); when(authorizationService.isDownloadable(any(), any())).thenReturn(true); @@ -224,7 +241,7 @@ public class ArchiveServiceTest { }).when(restTemplate).execute(eq("http://portal/base/url/portal-file"), eq(HttpMethod.GET), any(RequestCallback.class), any(ResponseExtractor.class), any(Object[].class)); - archiveService.createArchive(job); + archiveService.createArchive(job, servletRequest); File result = tmpDir.toPath().resolve("user1").resolve("abcdef." + extension).toFile(); @@ -239,7 +256,7 @@ public class ArchiveServiceTest { TestArchiveHandler<I, E> testArchiveHandler = testArchiveGetter.apply(new FileInputStream(result)); - try ( InputStream is = testArchiveHandler.getInputStream()) { + try (InputStream is = testArchiveHandler.getInputStream()) { E entry; while ((entry = testArchiveHandler.getNextEntry()) != null) { assertFalse(i >= expectedSequence.size(), "Found more entries than in expected sequence"); @@ -276,12 +293,16 @@ public class ArchiveServiceTest { private File createFile(File parent, String path) throws Exception { File file = parent.toPath().resolve(path).toFile(); - file.getParentFile().mkdirs(); + file.getParentFile().mkdirs(); file.createNewFile(); Files.write(file.toPath(), "some data".getBytes()); return file; } + private ArchiveEntryDescriptor getArchiveEntryDescriptor(String vosPath) { + return new ArchiveEntryDescriptor(vosPath); + } + /** * @TestPropertySource annotation can't be used in this test because we need * to set the generated.dir property dynamically (since the test directory