diff --git a/cds-plugin/src/main/java/org/entando/entando/plugins/jpcds/aps/system/storage/CdsStorageManager.java b/cds-plugin/src/main/java/org/entando/entando/plugins/jpcds/aps/system/storage/CdsStorageManager.java index 7c12b7b532..55969f867e 100644 --- a/cds-plugin/src/main/java/org/entando/entando/plugins/jpcds/aps/system/storage/CdsStorageManager.java +++ b/cds-plugin/src/main/java/org/entando/entando/plugins/jpcds/aps/system/storage/CdsStorageManager.java @@ -81,19 +81,18 @@ public void saveFile(String subPath, boolean isProtectedResource, InputStream is private void create(String subPath, boolean isProtectedResource, Optional fileInputStream) { try { - Optional config = getTenantConfig(); + Optional config = this.getTenantConfig(); if(StringUtils.isBlank(subPath)){ throw new EntRuntimeException(ERROR_VALIDATING_PATH_MSG); } this.validateAndReturnResourcePath(config, subPath, false, isProtectedResource); URI apiUrl = CdsUrlUtils.buildCdsInternalApiUrl(config, configuration, "/upload/"); - CdsCreateResponseDto response = caller.executePostCall(apiUrl, + CdsCreateResponseDto response = this.caller.executePostCall(apiUrl, subPath, isProtectedResource, fileInputStream, config, false); - if (!response.isStatusOk()) { throw new EntRuntimeException("Invalid status - Response " + response.isStatusOk()); } @@ -112,8 +111,8 @@ public void deleteDirectory(String subPath, boolean isProtectedResource) throws @Override public boolean deleteFile(String subPath, boolean isProtectedResource) { try { - Optional config = getTenantConfig(); - if(StringUtils.isBlank(subPath)){ + Optional config = this.getTenantConfig(); + if (StringUtils.isBlank(subPath)){ throw new EntRuntimeException(ERROR_VALIDATING_PATH_MSG); } this.validateAndReturnResourcePath(config, subPath, true, isProtectedResource); @@ -123,7 +122,7 @@ public boolean deleteFile(String subPath, boolean isProtectedResource) { .path(CdsUrlUtils.getSection(isProtectedResource, config, this.configuration, true)) .path(subPath) .build(); - return caller.executeDeleteCall(apiUrl, config, false); + return this.caller.executeDeleteCall(apiUrl, config, false); } catch (EntRuntimeException ert) { throw ert; } catch (Exception e) { @@ -136,32 +135,31 @@ public InputStream getStream(String subPath, boolean isProtectedResource) throws final String ERROR_EXTRACTING_FILE = "Error extracting file"; URI url = null; try { - Optional config = getTenantConfig(); - if(StringUtils.isBlank(subPath)){ + Optional config = this.getTenantConfig(); + if (StringUtils.isBlank(subPath)) { throw new EntRuntimeException(ERROR_VALIDATING_PATH_MSG); } - + if (!this.exists(subPath, isProtectedResource)) { + throw new EntResourceNotFoundException( + String.format("File \"%s\", protected \"%s\", Not Found", subPath, isProtectedResource)); + } this.validateAndReturnResourcePath(config, subPath, true, isProtectedResource); - url = (isProtectedResource) ? CdsUrlUtils.buildCdsInternalApiUrl(config, configuration) : CdsUrlUtils.buildCdsExternalPublicResourceUrl(config, configuration); - url = EntUrlBuilder.builder() .url(url) .path(CdsUrlUtils.getSection(isProtectedResource, config, this.configuration, true)) .path(subPath).build(); - Optional is = caller.getFile(url, config, isProtectedResource); - return is.orElseThrow(IOException::new); - - } catch (EntRuntimeException ert) { + return is.orElse(new ByteArrayInputStream(new byte[0])); + } catch (EntResourceNotFoundException | EntRuntimeException ert) { throw ert; } catch (HttpClientErrorException e) { if (e.getStatusCode().equals(HttpStatus.NOT_FOUND)) { log.info("File Not found - uri {}", url); return null; - } + } throw new EntResourceNotFoundException(ERROR_EXTRACTING_FILE, e); } catch (Exception e) { throw new EntResourceNotFoundException(ERROR_EXTRACTING_FILE, e); @@ -171,7 +169,7 @@ public InputStream getStream(String subPath, boolean isProtectedResource) throws @Override public String getResourceUrl(String subPath, boolean isProtectedResource) { try { - Optional config = getTenantConfig(); + Optional config = this.getTenantConfig(); return this.validateAndReturnResourcePath(config, subPath, false, isProtectedResource); } catch (Exception e) { throw new EntRuntimeException("Error extracting resource url", e); @@ -187,7 +185,7 @@ public boolean exists(String subPath, boolean isProtectedResource) { // when frontend wants to retrieve public or protected folder contents it gets request with an empty subpath private boolean isSubPathPresent(String[] filenames, String subPath){ - if(StringUtils.isEmpty(subPath)) { + if (StringUtils.isEmpty(subPath)) { return filenames.length > 0; } else { return Arrays.asList(filenames).contains(subPath); @@ -250,7 +248,7 @@ private List listAttributes(String subPath, boolean isPr .path(CdsUrlUtils.getSection(isProtectedResource, config, this.configuration, true)) .path(subPath) .build(); - + Optional cdsFileList = caller.getFileAttributeView(apiUrl, config); return remapAndSort(cdsFileList, filter); diff --git a/cds-plugin/src/test/java/org/entando/entando/plugins/jpcds/aps/system/storage/CdsStorageManagerTest.java b/cds-plugin/src/test/java/org/entando/entando/plugins/jpcds/aps/system/storage/CdsStorageManagerTest.java index e76c391ec0..3323a9ed93 100644 --- a/cds-plugin/src/test/java/org/entando/entando/plugins/jpcds/aps/system/storage/CdsStorageManagerTest.java +++ b/cds-plugin/src/test/java/org/entando/entando/plugins/jpcds/aps/system/storage/CdsStorageManagerTest.java @@ -35,6 +35,7 @@ import org.entando.entando.aps.system.services.tenants.ITenantManager; import org.entando.entando.aps.system.services.tenants.TenantConfig; import org.entando.entando.ent.exception.EntException; +import org.entando.entando.ent.exception.EntResourceNotFoundException; import org.entando.entando.ent.exception.EntRuntimeException; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -238,13 +239,19 @@ void shouldManageErrorWhenCallGetStream() throws Exception { ).isInstanceOf(EntRuntimeException.class).hasMessageStartingWith("Error validating path"); String testFilePath = "/testfolder/test.txt"; + + Assertions.assertThatThrownBy( + ()-> cdsStorageManager.getStream(testFilePath, false) + ).isInstanceOf(EntResourceNotFoundException.class).hasMessageStartingWith("File \"" + testFilePath); + URI testFile = URI.create( baseUrl + "/custom-path" + testFilePath); + this.mockExecuteListFolder("http://cds-kube-service:8081/mytenant/api/v1/list/protected/testfolder", "test.txt"); Mockito.when(cdsRemoteCaller.getFile(eq(testFile), any(), eq(false))).thenReturn(null); Assertions.assertThatThrownBy( ()-> cdsStorageManager.getStream(testFilePath,false) - ).isInstanceOf(EntException.class).hasMessageStartingWith("Error extracting file"); + ).isInstanceOf(EntResourceNotFoundException.class).hasMessageStartingWith("Error extracting file"); String testFilePathBadGateway = "/testfolder/test-badgw.txt"; URI testFileBadGateway = URI.create( baseUrl + "/custom-path" + testFilePathBadGateway); @@ -256,15 +263,9 @@ void shouldManageErrorWhenCallGetStream() throws Exception { ()-> cdsStorageManager.getStream(testFilePathBadGateway,false) ).isInstanceOf(EntException.class).hasMessageStartingWith("Error extracting file"); - String testFilePathNotFound = "/testfolder/test-notfound.txt"; - URI testFileNotFound = URI.create( baseUrl + "/custom-path" + testFilePathNotFound); - Mockito.when(cdsRemoteCaller.getFile(eq(testFileNotFound), - any(), - eq(false))).thenThrow(new HttpClientErrorException(HttpStatus.NOT_FOUND)); - - Assertions.assertThat(cdsStorageManager.getStream(testFilePathNotFound,false)).isNull(); - + Assertions.assertThatThrownBy(() -> cdsStorageManager.getStream(testFilePathNotFound,false)) + .isInstanceOf(EntResourceNotFoundException.class).hasMessageStartingWith("Error extracting file"); } @Test @@ -276,20 +277,22 @@ void shouldReturnDataWhenCallGetStream() throws Exception { "cdsPath","/mytenant/api/v1/"); TenantConfig tc = new TenantConfig(configMap); Mockito.when(tenantManager.getConfig("my-tenant")).thenReturn(Optional.ofNullable(tc)); - + Mockito.when(cdsRemoteCaller.getFile(eq(URI.create("http://my-server/tenant1/cms-resources/test-folder/test.txt")), any(), eq(false))).thenReturn(Optional.ofNullable(new ByteArrayInputStream("text random".getBytes(StandardCharsets.UTF_8)))); - + + this.mockExecuteListFolder("http://cds-kube-service:8081/mytenant/api/v1/list/test-folder", "test.txt"); ApsTenantApplicationUtils.setTenant("my-tenant"); InputStream is = cdsStorageManager.getStream(testFilePath,false); Assertions.assertThat(new BufferedReader(new InputStreamReader(is)) .lines().collect(Collectors.joining(""))).isEqualTo("text random"); - + Mockito.when(cdsRemoteCaller.getFile(eq(URI.create("http://cds-kube-service:8081/mytenant/api/v1/protected/test-folder/test.txt")), any(), eq(true))).thenReturn(Optional.ofNullable(new ByteArrayInputStream("text random".getBytes(StandardCharsets.UTF_8)))); - + + this.mockExecuteListFolder("http://cds-kube-service:8081/mytenant/api/v1/list/protected/test-folder", "test.txt"); is = cdsStorageManager.getStream(testFilePath,true); Assertions.assertThat(new BufferedReader(new InputStreamReader(is)) .lines().collect(Collectors.joining(""))).isEqualTo("text random"); @@ -466,7 +469,7 @@ void shouldReadFile() throws Exception { String testFilePath = "/testfolder/test.txt"; Map configMap = Map.of("cdsPublicUrl","http://my-server/tenant1/cms-resources", - "cdsPrivateUrl","http://cds-kube-service:8081/", + "cdsPrivateUrl","http://cdsmaster-kube-service:8081/", "cdsPath","/mytenant/api/v1/"); TenantConfig tc = new TenantConfig(configMap); Mockito.when(tenantManager.getConfig("my-tenant")).thenReturn(Optional.ofNullable(tc)); @@ -474,8 +477,9 @@ void shouldReadFile() throws Exception { Mockito.when(cdsRemoteCaller.getFile(any(), any(), eq(false))).thenReturn(Optional.ofNullable(new ByteArrayInputStream("text random".getBytes(StandardCharsets.UTF_8)))); - - + + this.mockExecuteListFolder("http://cdsmaster-kube-service:8081/mytenant/api/v1/list/testfolder", "test.txt"); + ApsTenantApplicationUtils.setTenant("my-tenant"); Assertions.assertThat(cdsStorageManager.readFile(testFilePath,false)) .isEqualTo("text random"); @@ -483,7 +487,7 @@ void shouldReadFile() throws Exception { @Test void shouldManageExceptionWhenReadFile() throws Exception { - String testFilePath = "/testfolder/test.txt"; + String testFilePath = "/testfolder/subfolder/test.txt"; Map configMap = Map.of("cdsPublicUrl","http://my-server/tenant1/cms-resources", "cdsPrivateUrl","http://cds-kube-service:8081/", @@ -500,7 +504,7 @@ void shouldManageExceptionWhenReadFile() throws Exception { try (MockedStatic ioUtils = Mockito.mockStatic(IOUtils.class)) { ioUtils.when(() -> IOUtils.toString(any(InputStream.class), eq(StandardCharsets.UTF_8))) .thenThrow(new IOException()); - + this.mockExecuteListFolder("http://cds-kube-service:8081/mytenant/api/v1/list/testfolder/subfolder", "test.txt"); Assertions.assertThatThrownBy(() -> cdsStorageManager.readFile(testFilePath, false)) .isInstanceOf(EntException.class) .hasMessageStartingWith("Error extracting text"); @@ -585,5 +589,12 @@ void testListAttributes() throws Throwable { } assertTrue(containsCms); } + + private void mockExecuteListFolder(String uri, String filename) { + CdsFileAttributeViewDto file = new CdsFileAttributeViewDto(); + file.setName(filename); + Mockito.when(cdsRemoteCaller.getFileAttributeView(eq(URI.create(uri)), + any())).thenReturn(Optional.ofNullable(new CdsFileAttributeViewDto[]{file})); + } } diff --git a/engine/src/main/java/org/entando/entando/aps/system/init/DatabaseManager.java b/engine/src/main/java/org/entando/entando/aps/system/init/DatabaseManager.java index ed6e1af35e..af843252d8 100644 --- a/engine/src/main/java/org/entando/entando/aps/system/init/DatabaseManager.java +++ b/engine/src/main/java/org/entando/entando/aps/system/init/DatabaseManager.java @@ -70,6 +70,7 @@ import org.entando.entando.ent.util.EntLogging.EntLogFactory; import org.entando.entando.ent.util.EntLogging.EntLogger; import org.springframework.beans.factory.ListableBeanFactory; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.io.Resource; import org.springframework.web.context.ServletContextAware; @@ -96,6 +97,9 @@ public class DatabaseManager extends AbstractInitializerManager private int lockFallbackMinutes; private ServletContext servletContext; + + @Autowired + private ITenantManager tenantManager; public void init() { logger.debug("DatabaseManager ready"); @@ -568,7 +572,7 @@ public boolean dropAndRestoreBackup(String subFolderName) throws EntException { return false; } //TODO future improvement - execute 'lifeline' backup - this.getDatabaseRestorer().dropAndRestoreBackup(subFolderName); + this.getDatabaseRestorer().dropAndRestoreBackup(subFolderName, this.tenantManager); ApsWebApplicationUtils.executeSystemRefresh(this.getServletContext()); return true; } catch (Throwable t) { @@ -586,7 +590,7 @@ private boolean restoreBackup(String subFolderName) throws EntException { logger.error("backup not available - subfolder '{}'", subFolderName); return false; } - this.getDatabaseRestorer().restoreBackup(subFolderName); + this.getDatabaseRestorer().restoreBackup(subFolderName, this.tenantManager); return true; } catch (Throwable t) { logger.error("Error while restoring local backup", t); @@ -594,10 +598,16 @@ private boolean restoreBackup(String subFolderName) throws EntException { } } - private String[] extractBeanNames(Class beanClass) { + private String[] extractBeanNames(Class beanClass) { ListableBeanFactory factory = (ListableBeanFactory) this.getBeanFactory(); return factory.getBeanNamesForType(beanClass); } + + + + + + @Override public InputStream getTableDump(String tableName, String dataSourceName, String subFolderName) throws EntException { diff --git a/engine/src/main/java/org/entando/entando/aps/system/init/DatabaseRestorer.java b/engine/src/main/java/org/entando/entando/aps/system/init/DatabaseRestorer.java index 5d127bb04e..94963851dd 100644 --- a/engine/src/main/java/org/entando/entando/aps/system/init/DatabaseRestorer.java +++ b/engine/src/main/java/org/entando/entando/aps/system/init/DatabaseRestorer.java @@ -13,6 +13,7 @@ */ package org.entando.entando.aps.system.init; +import com.agiletec.aps.util.ApsTenantApplicationUtils; import org.entando.entando.ent.exception.EntException; import com.agiletec.aps.util.FileTextReader; @@ -26,6 +27,7 @@ import org.entando.entando.aps.system.init.model.Component; import org.entando.entando.aps.system.init.util.QueryExtractor; import org.entando.entando.aps.system.init.util.TableDataUtils; +import org.entando.entando.aps.system.services.tenants.ITenantManager; import org.entando.entando.ent.util.EntLogging.EntLogger; import org.entando.entando.ent.util.EntLogging.EntLogFactory; @@ -50,22 +52,24 @@ protected void initOracleSchema(DataSource dataSource) throws Throwable { } } - protected void dropAndRestoreBackup(String backupSubFolder) throws EntException { + protected void dropAndRestoreBackup(String backupSubFolder, + ITenantManager tenantManager) throws EntException { try { List components = this.getComponents(); int size = components.size(); for (int i = 0; i < components.size(); i++) { Component componentConfiguration = components.get(size - i - 1); - this.dropTables(componentConfiguration.getTableNames()); + this.dropTables(componentConfiguration.getTableNames(), tenantManager); } - this.restoreBackup(backupSubFolder); + this.restoreBackup(backupSubFolder, tenantManager); } catch (Throwable t) { _logger.error("Error while restoring backup: {}", backupSubFolder, t); throw new EntException("Error while restoring backup", t); } } - private void dropTables(Map> tableMapping) throws EntException { + private void dropTables(Map> tableMapping, + ITenantManager tenantManager) throws EntException { if (null == tableMapping) { return; } @@ -77,7 +81,7 @@ private void dropTables(Map> tableMapping) throws EntExcept if (null == tableNames || tableNames.isEmpty()) { continue; } - DataSource dataSource = (DataSource) this.getBeanFactory().getBean(dataSourceName); + DataSource dataSource = this.fetchDataSource(dataSourceName, tenantManager); int size = tableNames.size(); for (int j = 0; j < tableNames.size(); j++) { String tableName = tableNames.get(size - j - 1); @@ -91,49 +95,54 @@ private void dropTables(Map> tableMapping) throws EntExcept } } - protected void restoreBackup(String backupSubFolder) throws EntException { + protected void restoreBackup(String backupSubFolder, + ITenantManager tenantManager) throws EntException { try { List components = this.getComponents(); for (int i = 0; i < components.size(); i++) { Component componentConfiguration = components.get(i); - this.restoreLocalDump(componentConfiguration.getTableNames(), backupSubFolder); + this.restoreLocalDump(componentConfiguration.getTableNames(), backupSubFolder, tenantManager); } } catch (Throwable t) { _logger.error("Error while restoring local backup", t); throw new EntException("Error while restoring local backup", t); } } - - private void restoreLocalDump(Map> tableMapping, String backupSubFolder) throws EntException { + + private void restoreLocalDump(Map> tableMapping, + String backupSubFolder, ITenantManager tenantManager) throws EntException { if (null == tableMapping) { return; } try { - StringBuilder folder = new StringBuilder(this.getLocalBackupsFolder()) - .append(backupSubFolder).append(File.separator); + String folder = this.getLocalBackupsFolder() + backupSubFolder + File.separator; String[] dataSourceNames = this.extractBeanNames(DataSource.class); - for (int i = 0; i < dataSourceNames.length; i++) { - String dataSourceName = dataSourceNames[i]; - List tableNames = tableMapping.get(dataSourceName); - if (null == tableNames || tableNames.isEmpty()) { - continue; - } - DataSource dataSource = (DataSource) this.getBeanFactory().getBean(dataSourceName); - this.initOracleSchema(dataSource); - for (int j = 0; j < tableNames.size(); j++) { - String tableName = tableNames.get(j); - String fileName = folder.toString() + dataSourceName + File.separator + tableName + ".sql"; - InputStream is = this.getStorageManager().getStream(fileName, true); - if (null != is) { - this.restoreTableData(is, dataSource); - } - } - } + for (String dataSourceName : dataSourceNames) { + List tableNames = tableMapping.get(dataSourceName); + if (null == tableNames || tableNames.isEmpty()) { + continue; + } + DataSource dataSource = this.fetchDataSource(dataSourceName, tenantManager); + this.initOracleSchema(dataSource); + for (int j = 0; j < tableNames.size(); j++) { + String tableName = tableNames.get(j); + String fileName = folder + dataSourceName + File.separator + tableName + ".sql"; + InputStream is = this.getStorageManager().getStream(fileName, true); + if (null != is) { + this.restoreTableData(is, dataSource); + } + } + } } catch (Throwable t) { _logger.error("Error while restoring local dump", t); throw new RuntimeException("Error while restoring local dump", t); } } + + private DataSource fetchDataSource(String dataSourceName, ITenantManager tenantManager) { + return ApsTenantApplicationUtils.getTenant().map(tenantManager::getDatasource) + .orElse((DataSource) this.getBeanFactory().getBean(dataSourceName)); + } private void restoreTableData(InputStream is, DataSource dataSource) { try { diff --git a/engine/src/main/java/org/entando/entando/aps/system/services/database/DatabaseService.java b/engine/src/main/java/org/entando/entando/aps/system/services/database/DatabaseService.java index 57c797c24a..3c52cce826 100644 --- a/engine/src/main/java/org/entando/entando/aps/system/services/database/DatabaseService.java +++ b/engine/src/main/java/org/entando/entando/aps/system/services/database/DatabaseService.java @@ -38,6 +38,11 @@ import java.io.InputStream; import java.util.ArrayList; import java.util.List; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.Setter; +import org.entando.entando.web.common.exceptions.ValidationGenericException; +import org.springframework.validation.BindException; /** * @author E.Santoboni @@ -45,9 +50,15 @@ public class DatabaseService implements IDatabaseService { private final EntLogger logger = EntLogFactory.getSanitizedLogger(this.getClass()); + + private static final String REPORT_CODE_FIELD_NAME = "reportCode"; + @Getter(AccessLevel.PROTECTED)@Setter private IDatabaseManager databaseManager; + @Getter(AccessLevel.PROTECTED)@Setter private IComponentManager componentManager; + @Getter(AccessLevel.PROTECTED)@Setter + private boolean restoreEnabled; @Override public int getStatus() { @@ -82,7 +93,7 @@ public DumpReportDto getDumpReportDto(String reportCode) { DataSourceDumpReport report = this.getDatabaseManager().getBackupReport(reportCode); if (null == report) { logger.warn("no dump found with code {}", reportCode); - throw new ResourceNotFoundException(DatabaseValidator.ERRCODE_NO_DUMP_FOUND, "reportCode", reportCode); + throw new ResourceNotFoundException(DatabaseValidator.ERRCODE_NO_DUMP_FOUND, REPORT_CODE_FIELD_NAME, reportCode); } dtos = new DumpReportDto(report, this.getComponentManager()); } catch (ResourceNotFoundException r) { @@ -121,17 +132,22 @@ public void startDatabaseBackup() { throw new RestServerError("error starting backup", t); } } - + @Override public void startDatabaseRestore(String reportCode) { try { + if (!this.isRestoreEnabled()) { + BindException bindException = new BindException(this, REPORT_CODE_FIELD_NAME); + bindException.reject(DatabaseValidator.ERRCODE_RESTORE_NO_ACTIVE, new String[]{}, "database.restore.disabled"); + throw new ValidationGenericException(bindException); + } DataSourceDumpReport report = this.getDatabaseManager().getBackupReport(reportCode); if (null == report) { logger.warn("no dump found with code {}", reportCode); - throw new ResourceNotFoundException(DatabaseValidator.ERRCODE_NO_DUMP_FOUND, "reportCode", reportCode); + throw new ResourceNotFoundException(DatabaseValidator.ERRCODE_NO_DUMP_FOUND, REPORT_CODE_FIELD_NAME, reportCode); } this.getDatabaseManager().dropAndRestoreBackup(reportCode); - } catch (ResourceNotFoundException r) { + } catch (ValidationGenericException | ResourceNotFoundException r) { throw r; } catch (Throwable t) { logger.error("error starting restore", t); @@ -173,20 +189,4 @@ public byte[] getTableDump(String reportCode, String dataSource, String tableNam return bytes; } - public IDatabaseManager getDatabaseManager() { - return databaseManager; - } - - public void setDatabaseManager(IDatabaseManager databaseManager) { - this.databaseManager = databaseManager; - } - - public IComponentManager getComponentManager() { - return componentManager; - } - - public void setComponentManager(IComponentManager componentManager) { - this.componentManager = componentManager; - } - } diff --git a/engine/src/main/java/org/entando/entando/aps/system/services/storage/LocalStorageManager.java b/engine/src/main/java/org/entando/entando/aps/system/services/storage/LocalStorageManager.java index c64b407368..9c8fae05ce 100644 --- a/engine/src/main/java/org/entando/entando/aps/system/services/storage/LocalStorageManager.java +++ b/engine/src/main/java/org/entando/entando/aps/system/services/storage/LocalStorageManager.java @@ -17,8 +17,6 @@ import org.apache.commons.lang3.CharEncoding; import org.entando.entando.ent.exception.EntException; import org.entando.entando.ent.exception.EntRuntimeException; -import org.entando.entando.ent.util.EntLogging.EntLogFactory; -import org.entando.entando.ent.util.EntLogging.EntLogger; import java.io.*; import java.util.Arrays; @@ -52,7 +50,7 @@ public class LocalStorageManager implements IStorageManager, InitializingBean { private String protectedBaseURL; private String allowedEditExtensions; - + @Override public void afterPropertiesSet() throws Exception { logger.info("** Enabled Local Storage Manager **"); } diff --git a/engine/src/main/java/org/entando/entando/ent/exception/EntResourceNotFoundException.java b/engine/src/main/java/org/entando/entando/ent/exception/EntResourceNotFoundException.java index bf7d081e5c..da5e26891c 100644 --- a/engine/src/main/java/org/entando/entando/ent/exception/EntResourceNotFoundException.java +++ b/engine/src/main/java/org/entando/entando/ent/exception/EntResourceNotFoundException.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-Present Entando Inc. (http://www.entando.com) All rights reserved. + * Copyright 2023-Present Entando Inc. (http://www.entando.com) All rights reserved. * * This library is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free @@ -14,11 +14,16 @@ package org.entando.entando.ent.exception; /** - * Generic Entando Runtime Exception + * Generic Resource Not Found Exception */ public class EntResourceNotFoundException extends EntException { + public EntResourceNotFoundException(String message) { + super(message); + } + public EntResourceNotFoundException(String message, Throwable cause) { super(message, cause); } + } diff --git a/engine/src/main/java/org/entando/entando/web/database/validator/DatabaseValidator.java b/engine/src/main/java/org/entando/entando/web/database/validator/DatabaseValidator.java index 3ae63ba37b..204a14ebca 100644 --- a/engine/src/main/java/org/entando/entando/web/database/validator/DatabaseValidator.java +++ b/engine/src/main/java/org/entando/entando/web/database/validator/DatabaseValidator.java @@ -30,6 +30,7 @@ public class DatabaseValidator extends AbstractPaginationValidator { public static final String ERRCODE_NO_DUMP_FOUND = "1"; public static final String ERRCODE_NO_TABLE_DUMP_FOUND = "1"; + public static final String ERRCODE_RESTORE_NO_ACTIVE = "2"; @Override public boolean supports(Class type) { diff --git a/engine/src/main/resources/rest/messages.properties b/engine/src/main/resources/rest/messages.properties index 9db69e923f..4955556dca 100644 --- a/engine/src/main/resources/rest/messages.properties +++ b/engine/src/main/resources/rest/messages.properties @@ -283,6 +283,7 @@ userSettings.MaxMonthsSinceLastAccess.invalid=''lastAccessPasswordExpirationMont #Database database.dump.table.notFound=No table dump found for backup ''{0}'', database ''{1}'', table ''{2}'' +database.restore.disabled=Db Restore function disabled #Content plugins.jacms.content.model.invalidLangCode=Invalid content template language code diff --git a/engine/src/main/resources/spring/aps/servicesConfig.xml b/engine/src/main/resources/spring/aps/servicesConfig.xml index 330cd8553f..4a0fe88a80 100644 --- a/engine/src/main/resources/spring/aps/servicesConfig.xml +++ b/engine/src/main/resources/spring/aps/servicesConfig.xml @@ -143,6 +143,7 @@ + ${db.restore.enabled} diff --git a/engine/src/test/java/org/entando/entando/aps/system/init/DatabaseManagerTest.java b/engine/src/test/java/org/entando/entando/aps/system/init/DatabaseManagerTest.java index a53467a616..85f25de7b5 100644 --- a/engine/src/test/java/org/entando/entando/aps/system/init/DatabaseManagerTest.java +++ b/engine/src/test/java/org/entando/entando/aps/system/init/DatabaseManagerTest.java @@ -154,7 +154,7 @@ void testInstallDatabaseRestoreLastDump() throws Exception { dbFactory.when(DatabaseFactory::getInstance).thenReturn(Mockito.mock(DatabaseFactory.class)); databaseManager.installDatabase(null, DatabaseMigrationStrategy.AUTO, Optional.empty()); - Mockito.verify(databaseRestorer).restoreBackup("/path/to/dump"); + Mockito.verify(databaseRestorer).restoreBackup("/path/to/dump", this.tenantManager); } } diff --git a/engine/src/test/java/org/entando/entando/web/database/DatabaseControllerTest.java b/engine/src/test/java/org/entando/entando/web/database/DatabaseControllerTest.java index 43da972a63..61f564edde 100644 --- a/engine/src/test/java/org/entando/entando/web/database/DatabaseControllerTest.java +++ b/engine/src/test/java/org/entando/entando/web/database/DatabaseControllerTest.java @@ -13,6 +13,8 @@ */ package org.entando.entando.web.database; +import static org.hamcrest.CoreMatchers.is; + import com.agiletec.aps.system.services.user.UserDetails; import org.entando.entando.aps.system.init.DatabaseManager; import org.entando.entando.aps.system.init.IComponentManager; @@ -36,6 +38,7 @@ import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import org.junit.jupiter.api.BeforeEach; @@ -73,6 +76,7 @@ public void setUp() throws Exception { .build(); databaseService.setDatabaseManager(this.databaseManager); databaseService.setComponentManager(this.componentManager); + databaseService.setRestoreEnabled(true); controller.setDatabaseService(databaseService); } @@ -125,7 +129,7 @@ void startBackup() throws Exception { } @Test - void startRestore_1() throws Exception { + void requireRestore() throws Exception { UserDetails user = new OAuth2TestUtils.UserBuilder("jack_bauer", "0x24").grantedToRoleAdmin().build(); String accessToken = mockOAuthInterceptor(user); String xml = null; @@ -140,7 +144,7 @@ void startRestore_1() throws Exception { } @Test - void startRestore_2() throws Exception { + void requireRestoreWithoutBackup() throws Exception { UserDetails user = new OAuth2TestUtils.UserBuilder("jack_bauer", "0x24").grantedToRoleAdmin().build(); String accessToken = mockOAuthInterceptor(user); when(databaseManager.getBackupReport(ArgumentMatchers.anyString())).thenReturn(null); @@ -151,6 +155,20 @@ void startRestore_2() throws Exception { Mockito.verify(databaseService, Mockito.times(1)).startDatabaseRestore("reportCode"); Mockito.verify(databaseManager, Mockito.times(0)).dropAndRestoreBackup("reportCode"); } + + @Test + void requireRestoreWithRestoreDisabled() throws Exception { + UserDetails user = new OAuth2TestUtils.UserBuilder("jack_bauer", "0x24").grantedToRoleAdmin().build(); + String accessToken = mockOAuthInterceptor(user); + databaseService.setRestoreEnabled(false); + ResultActions result = mockMvc.perform( + put("/database/restoreBackup/{reportCode}", "reportCode").content("{}") + .header("Authorization", "Bearer " + accessToken)); + result.andExpect(status().isBadRequest()); + result.andExpect(jsonPath("$.errors.size()", is(1))); + result.andExpect(jsonPath("$.errors[0].code", is("2"))); + Mockito.verifyNoInteractions(this.databaseManager); + } @Test void deleteReport() throws Exception {