diff --git a/build.gradle.kts b/build.gradle.kts index 6641606..230d2e4 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -77,6 +77,7 @@ dependencies { implementation("org.apache.commons:commons-lang3:3.12.0") implementation("commons-validator:commons-validator:1.7") implementation("org.springframework.boot:spring-boot-configuration-processor") + implementation("com.iqser.red.commons:storage-commons:2.3.0") testImplementation("org.springframework.boot:spring-boot-starter-test") testImplementation("org.springframework.cloud:spring-cloud-starter-openfeign") testImplementation("org.projectlombok:lombok") @@ -88,6 +89,8 @@ dependencies { testImplementation("org.springframework.amqp:spring-rabbit-test") testImplementation("org.testcontainers:postgresql:1.18.3") testImplementation("com.github.dasniko:testcontainers-keycloak:2.5.0") + testImplementation("org.testcontainers:testcontainers:1.18.3") + testImplementation("org.testcontainers:junit-jupiter:1.18.3") testAnnotationProcessor("org.projectlombok:lombok") } diff --git a/src/main/java/com/knecon/fforesight/tenantusermanagement/config/StorageConfiguration.java b/src/main/java/com/knecon/fforesight/tenantusermanagement/config/StorageConfiguration.java new file mode 100644 index 0000000..8573643 --- /dev/null +++ b/src/main/java/com/knecon/fforesight/tenantusermanagement/config/StorageConfiguration.java @@ -0,0 +1,32 @@ +package com.knecon.fforesight.tenantusermanagement.config; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Lazy; + +import com.iqser.red.storage.commons.service.azure.AzureBlobStorageService; +import com.iqser.red.storage.commons.service.s3.S3StorageService; + +import lombok.Getter; + +@Configuration +@Getter +@ComponentScan("com.iqser.red.storage.commons") +public class StorageConfiguration { + + S3StorageService s3StorageService; + + AzureBlobStorageService azureBlobStorageService; + + @Autowired + public void setS3StorageService(@Lazy S3StorageService s3StorageService) { + this.s3StorageService = s3StorageService; + } + + @Autowired + public void setAzureBlobStorageService(@Lazy AzureBlobStorageService azureBlobStorageService) { + this.azureBlobStorageService = azureBlobStorageService; + } + +} diff --git a/src/main/java/com/knecon/fforesight/tenantusermanagement/service/TenantManagementService.java b/src/main/java/com/knecon/fforesight/tenantusermanagement/service/TenantManagementService.java index 142b4c5..ff80562 100644 --- a/src/main/java/com/knecon/fforesight/tenantusermanagement/service/TenantManagementService.java +++ b/src/main/java/com/knecon/fforesight/tenantusermanagement/service/TenantManagementService.java @@ -40,6 +40,7 @@ import com.knecon.fforesight.tenantcommons.model.DatabaseConnection; import com.knecon.fforesight.tenantcommons.model.S3StorageConnection; import com.knecon.fforesight.tenantcommons.model.SearchConnection; import com.knecon.fforesight.tenantcommons.model.TenantResponse; +import com.knecon.fforesight.tenantusermanagement.config.StorageConfiguration; import com.knecon.fforesight.tenantusermanagement.entity.AzureStorageConnectionEntity; import com.knecon.fforesight.tenantusermanagement.entity.DatabaseConnectionEntity; import com.knecon.fforesight.tenantusermanagement.entity.S3StorageConnectionEntity; @@ -62,7 +63,7 @@ import lombok.extern.slf4j.Slf4j; public class TenantManagementService implements TenantProvider { private static final Long MAX_WAIT_TIME = 60_000L; // 60 seconds - private static final String FRONTEND_URL_PROPERY = "frontendUrl"; + private static final String FRONTEND_URL_PROPERTY = "frontendUrl"; private static final Set SUPPORTED_DATABASES = Set.of("postgresql"); private final EncryptionDecryptionService encryptionService; @@ -74,6 +75,7 @@ public class TenantManagementService implements TenantProvider { private final TenantUserManagementProperties tenantUserManagementProperties; private final RealmService realmService; private final RabbitTemplate rabbitTemplate; + private final StorageConfiguration storageConfiguration; @Value("${fforesight.tenant-exchange.name}") private String tenantExchangeName; @@ -120,6 +122,8 @@ public class TenantManagementService implements TenantProvider { .build(); if (tenantRequest.getAzureStorageConnection() != null) { + testAzureConnection(tenantRequest.getAzureStorageConnection().getConnectionString()); + tenantEntity.setAzureStorageConnection(AzureStorageConnectionEntity.builder() .connectionString(encryptionService.encrypt(tenantRequest.getAzureStorageConnection().getConnectionString())) .containerName(tenantRequest.getAzureStorageConnection().getContainerName()) @@ -127,6 +131,8 @@ public class TenantManagementService implements TenantProvider { } if (tenantRequest.getS3StorageConnection() != null) { + testS3Connection(tenantRequest.getS3StorageConnection()); + tenantEntity.setS3StorageConnection(S3StorageConnectionEntity.builder() .key(tenantRequest.getS3StorageConnection().getKey()) .secret(encryptionService.encrypt(tenantRequest.getS3StorageConnection().getSecret())) @@ -241,7 +247,7 @@ public class TenantManagementService implements TenantProvider { if (!ObjectUtils.isEmpty(tenantUserManagementProperties.getPublicServerUrl())) { Map attributes = new HashMap<>(); - attributes.put(FRONTEND_URL_PROPERY, tenantUserManagementProperties.getPublicServerUrl()); + attributes.put(FRONTEND_URL_PROPERTY, tenantUserManagementProperties.getPublicServerUrl()); realm.setAttributes(attributes); } @@ -389,6 +395,7 @@ public class TenantManagementService implements TenantProvider { var azureStorageConnection = tenantRequest.getAzureStorageConnection(); if (azureStorageConnection != null) { + testAzureConnection(tenantRequest.getAzureStorageConnection().getConnectionString()); tenantEntity .setAzureStorageConnection(AzureStorageConnectionEntity.builder() .connectionString(encryptionService.encrypt(azureStorageConnection.getConnectionString())) @@ -398,6 +405,7 @@ public class TenantManagementService implements TenantProvider { var s3StorageConnection = tenantRequest.getS3StorageConnection(); if (s3StorageConnection != null) { + testS3Connection(s3StorageConnection); tenantEntity .setS3StorageConnection(S3StorageConnectionEntity.builder() .key(s3StorageConnection.getKey()) @@ -477,4 +485,29 @@ public class TenantManagementService implements TenantProvider { return tenantResponse; } + private void testAzureConnection(String connectionString) { + + var connection = storageConfiguration.getAzureBlobStorageService().testConnection(connectionString); + + if (!connection) { + throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Could not connect to Azure storage"); + } + } + + private void testS3Connection(S3StorageConnection s3StorageConnection) { + + var connection = storageConfiguration.getS3StorageService().testConnection( + s3StorageConnection.getKey(), + s3StorageConnection.getSecret(), + s3StorageConnection.getSignerType(), + s3StorageConnection.getBucketName(), + s3StorageConnection.getRegion(), + s3StorageConnection.getEndpoint() + ); + + if (!connection) { + throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Could not connect to S3 storage"); + } + } + } diff --git a/src/main/resources/application-dev.yaml b/src/main/resources/application-dev.yaml index 04b27fc..974d621 100644 --- a/src/main/resources/application-dev.yaml +++ b/src/main/resources/application-dev.yaml @@ -30,5 +30,7 @@ fforesight: springdoc: auth-server-url: http://localhost:8080 +storage: + backend: both cors.enabled: true diff --git a/src/test/java/com/knecon/fforesight/AbstractTenantUserManagementIntegrationTest.java b/src/test/java/com/knecon/fforesight/AbstractTenantUserManagementIntegrationTest.java index 836ea8a..95aa79e 100644 --- a/src/test/java/com/knecon/fforesight/AbstractTenantUserManagementIntegrationTest.java +++ b/src/test/java/com/knecon/fforesight/AbstractTenantUserManagementIntegrationTest.java @@ -18,6 +18,7 @@ import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit.jupiter.SpringExtension; +import org.testcontainers.containers.GenericContainer; import com.knecon.fforesight.feigntestclients.external.TenantsClient; import com.knecon.fforesight.feigntestclients.internal.InternalTenantsClient; @@ -44,9 +45,24 @@ public class AbstractTenantUserManagementIntegrationTest { public final static String TEST_TENANT_ID = "test_tenant_fforesight"; + private static GenericContainer minioServer; + + public static int minioPort; + + @BeforeAll + static void setupMinioContainer() { + minioServer = new GenericContainer("minio/minio") + .withEnv("MINIO_ACCESS_KEY", "minioadmin") + .withEnv("MINIO_SECRET_KEY", "minioadmin") + .withCommand("server /entity") + .withExposedPorts(9000); + minioServer.start(); + minioPort = minioServer.getFirstMappedPort(); + } + @BeforeEach public void createTestTenant(){ - testTenantService.createTestTenantIfNotExists(TEST_TENANT_ID); + testTenantService.createTestTenantIfNotExists(TEST_TENANT_ID, minioPort); } @Slf4j diff --git a/src/test/java/com/knecon/fforesight/tests/GeneralSettingsTest.java b/src/test/java/com/knecon/fforesight/tests/GeneralSettingsTest.java index 0717a84..ec93143 100644 --- a/src/test/java/com/knecon/fforesight/tests/GeneralSettingsTest.java +++ b/src/test/java/com/knecon/fforesight/tests/GeneralSettingsTest.java @@ -2,8 +2,16 @@ package com.knecon.fforesight.tests; import static org.assertj.core.api.Assertions.assertThat; +import java.time.Duration; + +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; +import org.testcontainers.Testcontainers; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.startupcheck.OneShotStartupCheckStrategy; +import org.testcontainers.containers.wait.strategy.HttpWaitStrategy; +import org.testcontainers.junit.jupiter.Container; import com.knecon.fforesight.AbstractTenantUserManagementIntegrationTest; import com.knecon.fforesight.feigntestclients.external.GeneralSettingsClient; diff --git a/src/test/java/com/knecon/fforesight/tests/TenantsTest.java b/src/test/java/com/knecon/fforesight/tests/TenantsTest.java index 8f4ed01..e2579ce 100644 --- a/src/test/java/com/knecon/fforesight/tests/TenantsTest.java +++ b/src/test/java/com/knecon/fforesight/tests/TenantsTest.java @@ -1,6 +1,7 @@ package com.knecon.fforesight.tests; import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; import java.util.Map; import java.util.Set; @@ -19,6 +20,8 @@ import com.knecon.fforesight.tenantcommons.model.SearchConnection; import com.knecon.fforesight.tenantusermanagement.model.TenantRequest; import com.knecon.fforesight.utils.TestTenantService; +import feign.FeignException; + public class TenantsTest extends AbstractTenantUserManagementIntegrationTest { @Autowired @@ -34,7 +37,7 @@ public class TenantsTest extends AbstractTenantUserManagementIntegrationTest { @Test public void testCreateNewTenant() { - testTenantService.createTestTenantIfNotExists("new_tenant"); + testTenantService.createTestTenantIfNotExists("new_tenant", minioPort); TenantContext.setTenantId("new_tenant"); @@ -45,13 +48,14 @@ public class TenantsTest extends AbstractTenantUserManagementIntegrationTest { assertThat(tenant.getGuid()).isNotBlank(); assertThat(tenantsClient.getTenants().stream().anyMatch(t -> t.getTenantId().equals("new_tenant"))).isTrue(); + TenantContext.clear(); } @Test public void testUpdateTenant() { - testTenantService.createTestTenantIfNotExists("new_tenant"); + testTenantService.createTestTenantIfNotExists("new_tenant", minioPort); TenantContext.setTenantId("new_tenant"); var tenantRequest = TenantRequest.builder() @@ -76,6 +80,27 @@ public class TenantsTest extends AbstractTenantUserManagementIntegrationTest { .password("updated_pwd") .username("updated_username") .build()) + .build(); + + var tenantEntity = tenantsClient.updateTenant("new_tenant", tenantRequest); + + tenantRequest.getSearchConnection().setPassword(encryptionService.encrypt("updated_pwd")); + tenantRequest.getDatabaseConnection().setPassword(encryptionService.encrypt("updated_pwd")); + + assertThat(tenantEntity.getDisplayName()).isEqualTo(tenantRequest.getDisplayName()); + assertThat(tenantEntity.getSearchConnection()).isEqualTo(tenantRequest.getSearchConnection()); + assertThat(tenantEntity.getDatabaseConnection()).isEqualTo(tenantRequest.getDatabaseConnection()); + + TenantContext.clear(); + } + + @Test + public void testUpdateTenantWithIncorrectS3Storage() { + testTenantService.createTestTenantIfNotExists("new_tenant", minioPort); + TenantContext.setTenantId("new_tenant"); + + var tenantRequest = TenantRequest.builder() + .tenantId("new_tenant") .s3StorageConnection(S3StorageConnection.builder() .key("updated_key") .bucketName("updated_bucket") @@ -84,24 +109,29 @@ public class TenantsTest extends AbstractTenantUserManagementIntegrationTest { .secret("updated_secret") .signerType("updated_signer") .build()) + .build(); + + var exception = assertThrows(FeignException.BadRequest.class, () -> tenantsClient.updateTenant("new_tenant", tenantRequest)); + assertThat(exception.getMessage()).contains("Could not connect to S3 storage"); + + TenantContext.clear(); + } + + @Test + public void testUpdateTenantWithIncorrectAzureStorage() { + testTenantService.createTestTenantIfNotExists("new_tenant", minioPort); + TenantContext.setTenantId("new_tenant"); + + var tenantRequest = TenantRequest.builder() + .tenantId("new_tenant") .azureStorageConnection(AzureStorageConnection.builder() .connectionString("updated_connection") .containerName("updated_container") .build()) .build(); - var tenantEntity = tenantsClient.updateTenant("new_tenant", tenantRequest); - - tenantRequest.getS3StorageConnection().setSecret(encryptionService.encrypt("updated_secret")); - tenantRequest.getAzureStorageConnection().setConnectionString(encryptionService.encrypt("updated_connection")); - tenantRequest.getSearchConnection().setPassword(encryptionService.encrypt("updated_pwd")); - tenantRequest.getDatabaseConnection().setPassword(encryptionService.encrypt("updated_pwd")); - - assertThat(tenantEntity.getDisplayName()).isEqualTo(tenantRequest.getDisplayName()); - assertThat(tenantEntity.getS3StorageConnection()).isEqualTo(tenantRequest.getS3StorageConnection()); - assertThat(tenantEntity.getAzureStorageConnection()).isEqualTo(tenantRequest.getAzureStorageConnection()); - assertThat(tenantEntity.getSearchConnection()).isEqualTo(tenantRequest.getSearchConnection()); - assertThat(tenantEntity.getDatabaseConnection()).isEqualTo(tenantRequest.getDatabaseConnection()); + var exception = assertThrows(FeignException.BadRequest.class, () -> tenantsClient.updateTenant("new_tenant", tenantRequest)); + assertThat(exception.getMessage()).contains("Could not connect to Azure storage"); TenantContext.clear(); } diff --git a/src/test/java/com/knecon/fforesight/utils/TestTenantService.java b/src/test/java/com/knecon/fforesight/utils/TestTenantService.java index 0d72fa9..3ddf923 100644 --- a/src/test/java/com/knecon/fforesight/utils/TestTenantService.java +++ b/src/test/java/com/knecon/fforesight/utils/TestTenantService.java @@ -29,13 +29,13 @@ public class TestTenantService { private final TokenService tokenService; - public void createTestTenantIfNotExists(String testTenantId) { + public void createTestTenantIfNotExists(String testTenantId, int actualPort) { try { var tenantExists = internalTenantsResource.getTenant(testTenantId); assertThat(tenantExists.getGuid()).isNotBlank(); - }catch (Exception e) { + } catch (Exception e) { // not found var tenantRequest = TenantRequest.builder() .tenantId(testTenantId) @@ -52,7 +52,7 @@ public class TestTenantService { .password(SpringPostgreSQLTestContainer.getInstance().getPassword()) .build()) .searchConnection(SearchConnection.builder().hosts(Set.of("localhost")).port(9200).scheme("http").numberOfShards("1").numberOfReplicas("5").build()) - .s3StorageConnection(S3StorageConnection.builder().key("minioadmin").secret("minioadmin").bucketName("redaction").endpoint("http://localhost:9000").build()) + .s3StorageConnection(S3StorageConnection.builder().key("minioadmin").secret("minioadmin").bucketName("redaction").endpoint("http://localhost:" + actualPort).build()) .build(); var response = internalTenantsResource.createTenant(tenantRequest); diff --git a/src/test/resources/application.yaml b/src/test/resources/application.yaml index b9d246d..85591a5 100644 --- a/src/test/resources/application.yaml +++ b/src/test/resources/application.yaml @@ -127,6 +127,9 @@ fforesight: application-client-id: tenant-user-management swagger-client-secret: 'testSecret123!' +storage: + backend: both + cors.enabled: true springdoc: packages-to-scan: [ 'com.knecon.fforesight.tenantusermanagement.controller.external' ]